We're implementing SSL on top of Netty. But there's a flaw in current design. The client will retry connecting to server if failed. This is desired in Network or heavy server load issues. But wrong client credentials would cause constant failure.
There're some solutions:
The client-server connection can failover to unencrypted mode
(removing SslHandler from pipeline).
Client can die and throw exception on knowing it's SSL exception.
Unfortunately I don't know how to implement this using Netty. A few questions:
How to detect it's SSL exception?
How to remove the SslHandler from both side safely?
Please help out here.
As far as I know, there's no safe way to downgrade an SSL connection to a plaintext connection.
To detect a handshake failure, you need to implement userEventTriggered() handler method:
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
if (evt instanceof SslHandshakeCompletionEvent) {
if (!((SslHandshakeCompletionEvent) evt).isSuccess()) {
// handshake failed
}
}
}
You can also add a listener to the handshake future:
ChannelPipeline p = ...;
SslHandler sslHandler = p.get(SslHandler.class);
sslHandler.handshakeFuture().addListener(new FutureListener<Channel> { ... });
Related
I changed BIO to a non-blocking mode with
BIO_set_nbio(m_bio, 1)
for BIO_do_connect to not hang (and using BIO_should_retry and select to retry to reconnect). It solved my problem with connection to the wrong listener now fails immediately instead of timing out in 2 hours.
But now I have a new problem - SSL_get_verify_result always returns X509_V_OK. Doesn't matter if it is expired cert or just not the same as server cert - validation always succeeds.
What I don't understand is how and why non-blocking mode changes validation for a cert. I confirmed that without switching to a non-blocking mode validation fails for if the client cert not the same.
I tried to set client cert with both SSL_CTX_load_verify_locations and SSL_CTX_use_certificate_file and it doesn't seem to matter.
As Patrick pointed out the issue I had was due to SSL context in non-blocking mode didn't have server cert so verification immediately after establishing a connection always succeeded. To fix the issue I added cert validation (if it is the same I expect to see) after first read-write data exchange.
SSL_CTX_new(SSLv23_client_method());
SSL_CTX_load_verify_locations(ctx, certFilePath, NULL);
BIO_new_ssl_connect(ctx);
BIO_get_ssl(bio, &ssl);
BIO_set_conn_hostname(bio, hostname);
BIO_set_nbio(bio, 1);
auto err = BIO_do_connect(bio);
// check err and BIO_should_retry to retry as needed
// Problem I had was here - the call always returns X509_V_OK
// in non-blocking mode regardless of what cert I use on
// my (client) side.
// In IO blocking mode (without BIO_set_nbio call above) validation
// fails as expected for mismatching certs.
SSL_get_verify_result(ssl);
// Exchange some data with server with
// BIO_read/BIO_write
// And now we have server cert in ctx
auto clientCert = SSL_get_certificate(ssl);
auto serverCert = SSL_get_peer_certificate(ssl);
auto certsAreTheSame = X509_cmp(clientCert, serverCert) == 0;
This follows on from "How to access JMX interface in docker from outside?" which talks about setting up unencrypted JMX connections.
I could use either RMI or JMXMP which Glassfish uses.
There is a set of JVM options that are required and I'm looking for the changes I need to set up JMX with SSL:
com.sun.management.jmxremote=true
com.sun.management.jmxremote.local.only=false
com.sun.management.jmxremote.ssl=true
com.sun.management.jmxremote.authenticate=true
com.sun.management.jmxremote.port=12345
com.sun.management.jmxremote.rmi.port=12346
java.rmi.server.hostname=10.11.12.176
com.sun.management.jmxremote.access.file=/.secure/jmxremote.access
com.sun.management.jmxremote.password.file=/.secure/jmxremote.pass
com.sun.management.jmxremote.login.config=ldap-ad-config
java.net.preferIPv4Stack=true
com.sun.management.jmxremote.ssl.config.file=/.secure/jmxremotessl.properties
javax.net.ssl.keyStore=/config/app.jks
javax.net.ssl.keyStorePassword=teabag
javax.net.ssl.trustStore=/config/cacerts
javax.net.ssl.trustStorePassword=milk
The problem is the same:
java.rmi.ConnectException: Connection refused to host: 172.0.0.85; nested exception is
java.net.ConnectException: Operation timed out
That IP address is the internal IP address of the docker container. I assume that this is happening despite the java.rmi.server.hostname solution because it's on SSL.
I tried to reverse proxy the SSL to non-SSL with nginx but that failed with the error
java.rmi.ConnectIOException: non-JRMP server at remote endpoint
so I guess I should be forwarding extra headers in nginx.
I'm now attempting to set up JMXMP but the documentation on how to do it is pretty thin on the ground. There's a Spring implementation and a Glassfish implementation but not with findable docs (as yet) - so I'm adding the glassfish tag.
The answer is that I can set up my app to use JMXMP and configure it to implement TLS connections using these JVM options:
-Dcom.sun.management.jmxremote=true
-Dcom.sun.management.jmxremote.local.only=false
-Dcom.sun.management.jmxremote.authenticate=true
-Dcom.sun.management.jmxremote.ssl=true
-Dcom.sun.management.jmxremote.access.file=C:/dev/.secure/jmxremote.access
-Dcom.sun.management.jmxremote.password.file=C:/dev/.secure/jmxremote.pass
-Dcom.sun.management.jmxremote.login.config=spnego-server
-Djava.net.preferIPv4Stack=true
-Dcom.sun.management.jmxremote.ssl.config.file=/.secure/jmxremotessl.properties
however I have to code up a configuration class to launch the JMXConnectorServer like so:
#Configuration
public class JmxServer {
public JmxServer(
#Value("${jmx.remote.hostname}") final String hostname,
#Value("${jmx.remote.port}") final String port) {
try {
Map<String, Object> env = new HashMap<>();
env.put("jmx.remote.profiles", "TLS SASL/PLAIN");
env.put(JMXConnector.CREDENTIALS,
new String[] {"myusername", "password"});
JMXConnectorServerFactory.newJMXConnectorServer(
new JMXServiceURL(
String.format("service:jmx:jmxmp://%s:%s",
hostname, port)),
env,
ManagementFactory.getPlatformMBeanServer()
).start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
But this is only half of it. I am now wrangling with JConsole to get it to do JMXMP with TLS.
For that I'm following this fossil question from 2007 on the Oracle forums:
Can JConsole connect to a remote JMX agent using JMXMP and TLS?
but still struggling...
I am writing a TCP text protocol for a project that I am working on. One of the commands in the protocol is STARTTLS which should upgrade the connection to TLS and continue on. My code to upgrade the connection is similar to the answer in this question. The problem I am having is when I am upgrading the TLS connection, the tlsConn.Handshake will hang and never let go. There are some code samples below. Any help is greatly appreciated.
After the STARTTLS command is received...
// Init a new TLS connection. I need a *tls.Conn type
// so that I can do the Handshake()
s.Logf("++> Upgrading connection to TLS")
tlsConn := tls.Server(s.Conn, s.Server.TLSConfig)
s.Logf("++> Attempting TLS Handshake")
tlsConn.Handshake()
s.Logf("++> TLS Handshake Successful")
// Here is the trick. Since I do not need to access
// any of the TLS functions anymore,
// I can convert tlsConn back in to a net.Conn type
s.Conn = net.Conn(tlsConn)
s.Logf("++> Updating read/write buffers")
s.reader = textproto.NewReader(bufio.NewReader(s.Conn))
s.writer = textproto.NewWriter(bufio.NewWriter(s.Conn))
s.Printf("100 SUCCESS")
The client is currently upgrading the connection right after it sends the STARTTLS command like this...
c.conn = tls.Client(c.conn, clientTLSConfig)
The server *tls.Config looks like this...
// Load the key and certificate - paths are provided in flags.
cert, err := tls.LoadX509KeyPair(flagTLSCert, flagTLSKey)
if err != nil {
log.Fatal(err)
}
// Create the TLS config
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
ClientAuth: tls.VerifyClientCertIfGiven,
ServerName: fqdn(),
}
The client *tls.Config looks like this...
clientTLSConfig := &tls.Config{
InsecureSkipVerify: true,
}
Do you call c.conn.Handshake() or do something else to initiate the TLS handshake on the client side?
If the client does not initiate the handshake by sending TLS Client Hello, the server will sit forever waiting for it.
This is my best guess as you did not provide much of the client side code. Also checking with tcpdump would help to narrow the problem down (to server or client side).
I am developing a reverse proxy using http core nio 4.3.3 and need to connect to a Secure/HTTPS endpoint via the proxy. I took the reverse proxy(Asynchronous HTTP reverse proxy)[1] and added SSL support as shown below.
SSLContext clientSSLContext =
SSLUtil.createClientSSLContext(TRUST_STORE_LOCATION,
TRUST_STORE_PASSWORD);
final IOEventDispatch connectingEventDispatch =
new DefaultHttpClientIODispatch(
clientHandler,
clientSSLContext,
ConnectionConfig.DEFAULT);
...
connectingIOReactor.execute(connectingEventDispatch);
When I send the request, I am getting this error,
java.io.IOException: SSL not supported
The Stack trace is given below.
[client<-proxy] 00000001 java.io.IOException: SSL not supported
java.io.IOException: SSL not supported
at org.apache.http.impl.nio.pool.BasicNIOConnFactory.create(BasicNIOConnFactory.java:159)
at org.apache.http.impl.nio.pool.BasicNIOConnFactory.create(BasicNIOConnFactory.java:1)
at org.apache.http.nio.pool.AbstractNIOConnPool.requestCompleted(AbstractNIOConnPool.java:484)
at org.apache.http.nio.pool.AbstractNIOConnPool$InternalSessionRequestCallback.completed(AbstractNIOConnPool.java:770)
at org.apache.http.impl.nio.reactor.SessionRequestImpl.completed(SessionRequestImpl.java:127)
at org.apache.http.impl.nio.reactor.AbstractIOReactor.processNewChannels(AbstractIOReactor.java:423)
at org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:288)
at org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:105)
at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:586)
at java.lang.Thread.run(Thread.java:662)
I enabled SSL debug logs as well, but still could not figure out the issue.
Then I debugged this and found out that the proxy received the request from the client and drops due to an exception inside the handle method of HttpAsyncRequestConsumer. The exception is java.io.IOException: SSL not supported
Also note that the SSLContext was working fine with a reverse proxy written using the netty transport.
Any help would be appreciated.
[1] https://hc.apache.org/httpcomponents-core-ga/examples.html
Regards,
Ravindra.
When using a connection pool on the client side to manage outgoing connections one needs to ensure that the connection factory used by the pool to create new connection objects is SSL capable. Please make sure that the connection pool is properly configured.
Thanks a lot for the advice. That solved the issue.
clientSSLContext =
SSLUtil.createClientSSLContext(TRUST_STORE_LOCATION,
TRUST_STORE_PASSWORD);
BasicNIOConnFactory connectionFactory =
new BasicNIOConnFactory(
clientSSLContext,
null,
ConnectionConfig.DEFAULT);
proxyConnPool = new ProxyConnPool(connectingIOReactor, connectionFactory, 5000)
I am getting below error while connecting to SSL enabled queue in Websphere MQ using 7.1 jars through JMS.
Code :
public MQMessageSender(String channelName, String hostName,
String portNumber, String queueManagerName, String queueName) {
// Create a MQQueue Factory Object
try {
mqConnectionFact = new MQQueueConnectionFactory();
mqConnectionFact.setChannel("xxx.SVRCONN");
mqConnectionFact.setPort(Integer.parseInt("xxxx"));
mqConnectionFact.setHostName("host1");
//mqConnectionFact.setConnectionNameList("host2(port2)");
mqConnectionFact.setClientReconnectOptions(JMSC.MQCNO_RECONNECT_Q_MGR);
mqConnectionFact.setQueueManager("XXXXX");
mqConnectionFact.setTransportType(JMSC.MQJMS_TP_CLIENT_MQ_TCPIP);
mqConnectionFact.setSSLCipherSuite("SSL_RSA_WITH_RC4_128_SHA");
qConnection = mqConnectionFact.createQueueConnection();
qConnection.start();
qSession = qConnection.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
requestQueue = qSession.createQueue(queueName);
} catch (Exception exception) {
exception.printStackTrace();
}
}
Error :
Caused by: com.ibm.mq.MQException: JMSCMQ0001: WebSphere MQ call failed with
compcode '2' ('MQCC_FAILED') reason '2399' ('MQRC_SSL_PEER_NAME_ERROR').
at com.ibm.msg.client.wmq.common.internal.Reason.createException(Reason.java:209)
... 8 more
Caused by: com.ibm.mq.jmqi.JmqiException: CC=2;RC=2399;
AMQ9204: Connection to host 'host1' rejected.
[1=com.ibm.mq.jmqi.JmqiException[CC=2;RC=2399;
AMQ9640: SSL invalid peer name, channel '?', attribute 'OID.2.5.4.17 (x2)'.
[5=OID.2.5.4.17 (x2)]],3=host1(port1),5=RemotePeerName.setValue]
reason '2399' ('MQRC_SSL_PEER_NAME_ERROR') is reporting that is the error. See 2399 (095F) (RC2399): MQRC_SSL_PEER_NAME_ERROR. Additionally, you have the following error message:
AMQ9640: SSL invalid peer name, channel '?', attribute 'OID.2.5.4.17 (x2)'.
[5=OID.2.5.4.17 (x2)]],3=host1(port1),5=RemotePeerName.setValue]
which is telling you which part of the peerName was not recognised. It says, OID.2.5.4.17 which is Postal Code.
A brief Internet search reveals an APAR for this very problem, IC83494: WMQ V7.1: JMS CLIENT CONNECTION VIA SSL ENABLED CHANNEL FAILS WITH RC 2399 MQRC_SSL_PEER_NAME_ERROR.
This APAR is included in V7.1 FixPac 3.
There are some good pieces of advice and pointers link to from this article.https://www.ibm.com/developerworks/community/blogs/aimsupport/entry/websphere_mq_v7_java_and_jms_ssl_problems_and_solutions?lang=en
You've got reconnect enabled, was the problem on the first connect or when reconnecting?