Jetty ssl port not open for secure JSR-356 websock

2019-01-27 08:02发布

问题:

I'm writing a server app that uses secure websockets using embedded Jetty 9.3.0.M2. When I run it without secure sockets, everything is copacetic, but when I enable the secure sockets, my clients get connection refused and nmap shows that the port is closed. There are no errors in the log on the server side.

I believe that my .jks, .crt, .pem, and .key files and my keystore password are all correct, because other apps on this same server are using the same ones and are working.

Here is the code that launches the Jetty server. Everything works when it uses the regular socket.

if (keyStorePath != null) {
    // use secure sockets
    server = new Server();
    HttpConfiguration https = new HttpConfiguration();
    https.addCustomizer(new SecureRequestCustomizer());

    SslContextFactory sslContextFactory = new SslContextFactory();
    sslContextFactory.setKeyStorePath(keyStorePath);
    sslContextFactory.setKeyStorePassword(keyStorePassword);
    sslContextFactory.setKeyManagerPassword(keyStorePassword);
    ServerConnector sslConnector = new ServerConnector(server, 
            new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()), 
            new HttpConnectionFactory(https));
    sslConnector.setHost(serverName); // EDIT: this line was the problem, removing it fixed everything.
    sslConnector.setPort(port);
    server.setConnectors(new Connector[] { sslConnector });
} else {
    // use regular sockets
    server = new Server(port);
}

server.setStopAtShutdown(true);
server.setDumpAfterStart(false);
server.setDumpBeforeStop(false);

// Initialize JSR-356 style websocket
ServletContextHandler servletContextHandler = 
        new ServletContextHandler(ServletContextHandler.SESSIONS);
servletContextHandler.setContextPath(contextPath);
server.setHandler(servletContextHandler);
ServerContainer container = 
        WebSocketServerContainerInitializer.configureContext(servletContextHandler);
container.addEndpoint(MyWebsocketEndpoint.class);
server.start();
logger.info("Started server: " + server);
if (server.getConnectors().length > 0) {
    logger.info("Connector = " + server.getConnectors()[0] + 
            " isRunning=" + server.getConnectors()[0].isRunning());
}

When keyStorePath is not null (meaning use secure sockets), the log looks like this:

2015-04-23 16:07:37.634:INFO::main: Logging initialized @114ms
2015-04-23 16:07:37.863:INFO:oejs.Server:main: jetty-9.3.0.M2
2015-04-23 16:07:38.408:INFO:oejsh.ContextHandler:main: Started o.e.j.s.ServletContextHandler@3abd7ff4{/websockets,null,AVAILABLE}
2015-04-23 16:07:38.489:INFO:oejs.ServerConnector:main: Started ServerConnector@2e4996ea{SSL,[ssl, http/1.1]}{my.server.com:8085}
2015-04-23 16:07:38.490:INFO:oejs.Server:main: Started @973ms
Apr 23, 2015 4:07:38 PM com.crowdoptic.conference.jetty.JettyWebSocketServer start
INFO: Started server: org.eclipse.jetty.server.Server@7205c140
Apr 23, 2015 4:07:38 PM com.crowdoptic.conference.jetty.JettyWebSocketServer start
INFO: Connector = ServerConnector@2e4996ea{SSL,[ssl, http/1.1]}{my.server.com:8085} isRunning=true

nmap on port 8085 shows

PORT     STATE  SERVICE
8085/tcp closed unknown

The error in my JavaScript console is "Error in connection establishment: net::ERR_CONNECTION_REFUSED"

When keyStorePath is null (meaning use sockets), the log looks like this:

2015-04-23 16:15:19.624:INFO::main: Logging initialized @115ms
2015-04-23 16:15:19.847:INFO:oejs.Server:main: jetty-9.3.0.M2
2015-04-23 16:15:20.431:INFO:oejsh.ContextHandler:main: Started o.e.j.s.ServletContextHandler@403108f6{/websockets,null,AVAILABLE}
2015-04-23 16:15:20.446:INFO:oejs.ServerConnector:main: Started ServerConnector@4efce9a2{HTTP/1.1,[http/1.1]}{0.0.0.0:8085}
2015-04-23 16:15:20.450:INFO:oejs.Server:main: Started @941ms
Apr 23, 2015 4:15:20 PM com.crowdoptic.conference.jetty.JettyWebSocketServer start
INFO: Started server: org.eclipse.jetty.server.Server@57a20888
Apr 23, 2015 4:15:20 PM com.crowdoptic.conference.jetty.JettyWebSocketServer start
INFO: Connector = ServerConnector@4efce9a2{HTTP/1.1,[http/1.1]}{0.0.0.0:8085} isRunning=true

nmap on port 8085 shows

PORT     STATE  SERVICE
8085/tcp open   unknown

And the app works great from the browser. I'm stumped. I have tried many permutations of the code to set up the SSL, but to no avail. Thank you for looking at this.

Edited to make it clear that I'm using JSR-356 websockets rather than Jetty native websockets.

Edited put solution in comments of sample code.

回答1:

Jetty 9.3.0 is still undergoing changes and is unstable.

First, lets use a stable version of Jetty, namely 9.2.10.v20150310.

Your setup of SSL, the SslContextFactory, and the ServerConnector in your example are correct.

Example (using pre-created keystore file found in jetty-distribution/demo-base/etc):

package jetty.websocket;

import java.io.FileNotFoundException;
import java.net.URL;

import javax.websocket.OnMessage;
import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpoint;

import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;

public class SecureJavaxWebSocketServer
{
    @ServerEndpoint(value="/echo")
    public static class EchoSocket
    {
        @OnMessage
        public String onMessage(String msg)
        {
            return msg;
        }
    }

    public static void main(String[] args)
    {
        try
        {
            new SecureJavaxWebSocketServer().go();
        }
        catch (Throwable t)
        {
            t.printStackTrace(System.err);
        }
    }

    private URL findResource(String path) throws FileNotFoundException
    {
        URL url = Thread.currentThread().getContextClassLoader().getResource(path);
        if (url == null)
        {
            throw new FileNotFoundException("Resource Not Found: " + path);
        }
        return url;
    }

    public void go() throws Exception
    {
        Server server = new Server();
        int httpsPort = 9443;

        // Setup SSL
        URL keystore = findResource("ssl/keystore");

        SslContextFactory sslContextFactory = new SslContextFactory();
        sslContextFactory.setKeyStorePath(keystore.toExternalForm());
        sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
        sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
        sslContextFactory.addExcludeProtocols("SSLv3"); // a good thing to do
        sslContextFactory.addExcludeCipherSuites(".*_GCM_.*"); // geez these ciphers are slow

        // Setup HTTPS Configuration
        HttpConfiguration httpsConf = new HttpConfiguration();
        httpsConf.setSecurePort(httpsPort);
        httpsConf.setSecureScheme("https");
        httpsConf.addCustomizer(new SecureRequestCustomizer());

        ServerConnector htttpsConnector = new ServerConnector(server,
                new SslConnectionFactory(sslContextFactory,"http/1.1"),
                new HttpConnectionFactory(httpsConf));
        htttpsConnector.setPort(httpsPort);

        server.addConnector(htttpsConnector);

        // Establish base handler list
        HandlerList baseHandlers = new HandlerList();
        server.setHandler(baseHandlers);

        // Add Servlet Context
        ServletContextHandler context = new ServletContextHandler();
        context.setContextPath("/");
        baseHandlers.addHandler(context);

        // Add WebSocket
        ServerContainer jsrContainer = WebSocketServerContainerInitializer.configureContext(context);
        jsrContainer.addEndpoint(EchoSocket.class);

        // Add default handler (for errors and whatnot) - always last
        baseHandlers.addHandler(new DefaultHandler());

        // Lets see how the server is setup after it is started
        // server.setDumpAfterStart(true);

        try
        {
            // Start the server thread
            server.start();
            // Wait for the server thread to end
            server.join();
        }
        catch (Throwable t)
        {
            t.printStackTrace(System.err);
        }
    }
}

The startup console results:

2015-04-23 09:53:17.279:INFO::main: Logging initialized @139ms
2015-04-23 09:53:17.346:INFO:oejs.Server:main: jetty-9.2.10.v20150310
2015-04-23 09:53:17.370:INFO:oejsh.ContextHandler:main: Started o.e.j.s.ServletContextHandler@2437c6dc{/,null,AVAILABLE}
2015-04-23 09:53:17.574:INFO:oejs.ServerConnector:main: Started ServerConnector@71423665{SSL-http/1.1}{0.0.0.0:9443}
2015-04-23 09:53:17.575:INFO:oejs.Server:main: Started @437ms

The environment test:

$ netstat -tlnp | grep LISTEN | grep 9443
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
tcp6       0      0 :::9443                 :::*                    LISTEN      8918/java 

$ ps auwwwx | grep 8918
joakim    8918  0.7  0.1 11822896 59728 ?      Sl   09:53   0:00 /home/joakim/java/jvm/jdk-8u31-x64/bin/java -Dfile.encoding=UTF-8 -classpath /home/joakim/code/(..snip..) jetty.websocket.SecureWebSocketServer

You can test this echo class out using the echo test client at

http://www.websocket.org/echo.html

Just point the URL in the form to wss://localhost:9443/echo with the TLS checkbox ticked.