
Introduction
The current release of Javalin is 7.0.1. This release has upgraded its embedded Jetty server to from version 11 to version 12.1.
This article is not about Javalin - but you can see their v6 to v7 migration guide for more info on that.
Also, given I am upgrading an older Javalin application which predates Javalin’s
SSL plug-in, I am sticking with my custom Jetty configuration code. That code needs a few updates to handle Jetty API changes between v11 and v12.1.
Maven Artifacts
My new POM contains these Jetty-related entries:
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-java-server</artifactId>
<version>12.1.6</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>jetty-http2-common</artifactId>
<version>12.1.6</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>jetty-http2-server</artifactId>
<version>12.1.6</version>
</dependency>
|
One gotcha was that my old POM used the artifact jetty-alpn-server, instead of the one shown above (jetty-alpn-java-server). When used with Jetty 11, the original artifact was not a problem, but with Jetty 12.1 it causes an exception on start-up:
Exception in thread “main” java.lang.IllegalStateException: No Server ALPNProcessors!
at org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory.(ALPNServerConnectionFactory.java:47)
I think I was fortunate/lucky that the old way used to work for me. See here for some more background.
Forcing Secure Connections
This ensures that if someone enters a plain http URL, it will be automatically upgraded to https. My old code has been replaced by the following:
Java
1
2
3
|
SecurityHandler.PathMapped handler = new SecurityHandler.PathMapped();
handler.put("/*", Constraint.SECURE_TRANSPORT);
server.setHandler(handler);
|
This is a bit less verbose than what I was doing before.
Jetty Package Names
There have been some name changes and a general reorganization in Jetty, behind the scenes - but most of these changes do not affect my code.
I ended up with the following Jetty 12.1 set-up for my Javalin application:
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
import java.util.Properties;
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
import org.eclipse.jetty.http2.HTTP2Cipher;
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
import org.eclipse.jetty.security.Constraint;
import org.eclipse.jetty.security.SecurityHandler;
import org.eclipse.jetty.server.Connector;
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.util.ssl.SslContextFactory;
/**
* Configuration of the embedded Jetty server is controlled here, including
* activation of HTTPS and HTTP/2.
*/
public class DemoJetty {
private static final Server server = new Server();
private static final Properties props = DemoProperties.INSTANCE.getProps();
public static Server create() {
// apply the connectors:
server.setConnectors(new Connector[]{
createHttpConnector(),
createHttpsConnector()
});
// redirect all http to https:
return redirectHttpToHttps(server);
}
private static ServerConnector createHttpConnector() {
//
// HTTP (Insecure) Connector - all http traffic is redirected to https.
//
HttpConfiguration httpConfig = new HttpConfiguration();
httpConfig.setSendServerVersion(false);
httpConfig.addCustomizer(createSecureRequestCustomizer());
// for redirecting http to https:
httpConfig.setSecureScheme("https");
int httpsPort = Integer.parseInt(props.getProperty("demo.https.port"));
httpConfig.setSecurePort(httpsPort);
// Clear-Text HTTP/1.1:
// https://www.eclipse.org/jetty/documentation/jetty-12.1/programming-guide/index.html#pg-server-http-connector-protocol-http11
HttpConnectionFactory http11CF = new HttpConnectionFactory(httpConfig);
// create the connector:
ServerConnector httpConnector = new ServerConnector(server, http11CF);
httpConnector.setPort(Integer.parseInt(props.getProperty("demo.http.port")));
return httpConnector;
}
// https://jetty.org/docs/jetty/12.1/programming-guide/server/http.html#request-customizers
private static ServerConnector createHttpsConnector() {
//
// HTTPS (Secure) Connector
//
HttpConfiguration httpsConfig = new HttpConfiguration();
httpsConfig.setSendServerVersion(false);
httpsConfig.addCustomizer(createSecureRequestCustomizer()); // needed for TLS
// Encrypted HTTP/1.1:
HttpConnectionFactory h1 = new HttpConnectionFactory(httpsConfig);
// Encrypted HTTP/2:
HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(httpsConfig);
// ALPN - a TLS extension:
ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
alpn.setDefaultProtocol(h1.getProtocol()); // fallback protocol
// connection factory for TLS:
SslConnectionFactory ssl = new SslConnectionFactory(
getSslContextFactory(),
alpn.getProtocol()
);
// create the connector - the order of protocols is important (e.g. h2 is preferred over http1.1):
ServerConnector httpsConnector = new ServerConnector(
server,
ssl,
alpn,
h2,
h1
);
int httpsPort = Integer.parseInt(props.getProperty("demo.https.port"));
httpsConnector.setPort(httpsPort);
return httpsConnector;
}
private static SecureRequestCustomizer createSecureRequestCustomizer() {
// For the SecureRequestCustomizer, Jetty defaults are:
// - isSniRequired() -> false
// - isSniHostCheck() -> true
// - getStsMaxAge() -> -1 (no max age)
// - isStsIncludeSubDomains() -> false
// If you want to customize it, do something like this:
SecureRequestCustomizer secureRequestCustomizer = new SecureRequestCustomizer();
boolean sniHostCheck = Boolean.parseBoolean(props.getProperty("demo.jetty.snihostcheck"));
secureRequestCustomizer.setSniHostCheck(sniHostCheck);
return secureRequestCustomizer;
}
private static SslContextFactory.Server getSslContextFactory() {
SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setKeyStorePath(props.getProperty("demo.keystore.path"));
sslContextFactory.setKeyStorePassword(props.getProperty("demo.keystore.pass"));
sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR);
// These are weak, according to Jetty
// https://jetty.org/docs/jetty/12.1/operations-guide/protocols/index.html#ssl-customize-ciphers
// and https://www.ssllabs.com/ssltest/analyze.html:
sslContextFactory.addExcludeProtocols(
// By default, the SSL protocols (SSL, SSLv2, SSLv3, etc.)
// are already excluded by Jetty because they are vulnerable.
// https://docs.oracle.com/en/java/javase/21/docs/specs/security/standard-names.html#sslcontext-algorithms
"TLSv1",
"TLSv1.0",
"TLSv1.1"
);
// https://docs.oracle.com/en/java/javase/21/docs/specs/security/standard-names.html#jsse-cipher-suite-names
sslContextFactory.setExcludeCipherSuites(
"^TLS_RSA_.*$",
"^.*_RSA_.*_(MD5|SHA|SHA1)$",
"^.*_DHE_RSA_.*$",
"TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
"TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
"TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"
);
return sslContextFactory;
}
private static Server redirectHttpToHttps(Server server) {
//
// https://javadoc.jetty.org/jetty-12.1/org/eclipse/jetty/security/SecurityHandler.PathMapped.html
//
SecurityHandler.PathMapped handler = new SecurityHandler.PathMapped();
handler.put("/*", Constraint.SECURE_TRANSPORT);
server.setHandler(handler);
return server;
}
}
|
To Do…
I haven’t reviewed the excluded cipher suites in a while. As a next step, a visit to SSL Labs is overdue.