Skip to content

Commit 2220c83

Browse files
authored
Do not use privileged ports with WebServer by default (#4992)
Signed-off-by: jansupol <[email protected]>
1 parent dcfda44 commit 2220c83

File tree

13 files changed

+695
-42
lines changed

13 files changed

+695
-42
lines changed

containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved.
33
* Copyright (c) 2018 Markus KARG. All rights reserved.
44
*
55
* This program and the accompanying materials are made available under the
@@ -61,7 +61,7 @@ private GrizzlyHttpServer(final GrizzlyHttpContainer container, final JerseySeBo
6161

6262
this.container = container;
6363
this.httpServer = GrizzlyHttpServerFactory.createHttpServer(
64-
configuration.uri(false),
64+
configuration.uri(true),
6565
this.container,
6666
configuration.isHttps(),
6767
configuration.isHttps() ? new SSLEngineConfigurator(sslContext, false,

containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved.
33
* Copyright (c) 2018 Markus KARG. All rights reserved.
44
*
55
* This program and the accompanying materials are made available under the
@@ -57,7 +57,7 @@ final class JdkHttpServer implements WebServer {
5757

5858
this.container = container;
5959
this.httpServer = JdkHttpServerFactory.createHttpServer(
60-
configuration.uri(false),
60+
configuration.uri(true),
6161
this.container,
6262
configuration.sslContext(),
6363
sslClientAuthentication == OPTIONAL,

containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpServer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved.
33
* Copyright (c) 2018 Markus KARG. All rights reserved.
44
*
55
* This program and the accompanying materials are made available under the
@@ -68,7 +68,7 @@ final class JettyHttpServer implements WebServer {
6868
}
6969
this.container = container;
7070
this.httpServer = JettyHttpContainerFactory.createServer(
71-
configuration.uri(false),
71+
configuration.uri(true),
7272
sslContextFactory,
7373
this.container,
7474
configuration.autoStart());

containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpServer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved.
33
* Copyright (c) 2018 Markus KARG. All rights reserved.
44
*
55
* This program and the accompanying materials are made available under the
@@ -64,7 +64,7 @@ final class NettyHttpServer implements WebServer {
6464
final SeBootstrap.Configuration.SSLClientAuthentication sslClientAuthentication = configuration
6565
.sslClientAuthentication();
6666

67-
final URI uri = configuration.uri(false);
67+
final URI uri = configuration.uri(true);
6868
this.port = NettyHttpContainerProvider.getPort(uri);
6969

7070
this.container = container;

containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleHttpServer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved.
33
* Copyright (c) 2018 Markus KARG. All rights reserved.
44
*
55
* This program and the accompanying materials are made available under the
@@ -50,7 +50,7 @@ final class SimpleHttpServer implements WebServer {
5050
SimpleHttpServer(final SimpleContainer container, final JerseySeBootstrapConfiguration configuration) {
5151
this.container = container;
5252
this.simpleServer = SimpleContainerFactory.create(
53-
configuration.uri(false),
53+
configuration.uri(true),
5454
configuration.sslContext(),
5555
configuration.sslClientAuthentication(),
5656
this.container,

core-common/src/main/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModel.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public <T> T as(String name, Class<T> clazz) {
8686
}
8787
@Override
8888
public <T> Optional<T> getOptionalProperty(String name, Class<T> clazz) {
89-
return Optional.of(as(name, clazz));
89+
return Optional.ofNullable(as(name, clazz));
9090
}
9191

9292
@Override

core-server/src/main/java/org/glassfish/jersey/server/JerseySeBootstrapConfiguration.java

Lines changed: 117 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -18,19 +18,27 @@
1818

1919
import jakarta.ws.rs.SeBootstrap;
2020
import jakarta.ws.rs.core.UriBuilder;
21+
import org.glassfish.jersey.internal.config.ExternalPropertiesConfigurationFactory;
22+
import org.glassfish.jersey.internal.config.SystemPropertiesConfigurationModel;
23+
import org.glassfish.jersey.internal.util.PropertiesClass;
2124
import org.glassfish.jersey.server.internal.LocalizationMessages;
2225
import org.glassfish.jersey.server.spi.Container;
2326
import org.glassfish.jersey.server.spi.WebServer;
2427

2528
import javax.net.ssl.SSLContext;
29+
import java.io.IOException;
30+
import java.net.ServerSocket;
2631
import java.net.URI;
2732
import java.security.NoSuchAlgorithmException;
33+
import java.util.Collections;
2834
import java.util.HashMap;
2935
import java.util.Map;
3036
import java.util.Optional;
37+
import java.util.Random;
3138
import java.util.function.BiFunction;
3239
import java.util.logging.Logger;
3340

41+
import static java.lang.Boolean.FALSE;
3442
import static java.lang.Boolean.TRUE;
3543

3644
/**
@@ -40,6 +48,7 @@
4048
*/
4149
public final class JerseySeBootstrapConfiguration implements SeBootstrap.Configuration {
4250
private static final Logger LOGGER = Logger.getLogger(JerseySeBootstrapConfiguration.class.getName());
51+
protected static final Random RANDOM = new Random();
4352
private final SeBootstrap.Configuration configuration;
4453

4554
private JerseySeBootstrapConfiguration(SeBootstrap.Configuration configuration) {
@@ -61,16 +70,60 @@ public Object property(String name) {
6170
public URI uri(boolean resolveDefaultPort) {
6271
final String protocol = configuration.protocol();
6372
final String host = configuration.host();
64-
final int configPort = configuration.port();
65-
final int port = (configPort < 0 && resolveDefaultPort)
66-
? isHttps() ? Container.DEFAULT_HTTPS_PORT : Container.DEFAULT_HTTP_PORT
67-
: configPort;
73+
final int port = resolveDefaultPort ? resolvePort() : configuration.port();
6874
final String rootPath = configuration.rootPath();
6975
final URI uri = UriBuilder.newInstance().scheme(protocol.toLowerCase()).host(host).port(port).path(rootPath)
7076
.build();
7177
return uri;
7278
}
7379

80+
private int resolvePort() {
81+
final int configPort = configuration.port();
82+
final int basePort = allowPrivilegedPorts() ? 0 : 8000;
83+
final int port;
84+
switch (configPort) {
85+
case SeBootstrap.Configuration.DEFAULT_PORT:
86+
port = basePort + (isHttps() ? Container.DEFAULT_HTTPS_PORT : Container.DEFAULT_HTTP_PORT);
87+
break;
88+
case SeBootstrap.Configuration.FREE_PORT:
89+
port = _resolvePort(basePort == 0);
90+
break;
91+
default:
92+
port = configPort;
93+
break;
94+
}
95+
return port;
96+
}
97+
98+
private int _resolvePort(boolean allowPrivilegedPort) {
99+
final int basePort = allowPrivilegedPort ? 0 : 1023;
100+
// Get the initial range parameters
101+
final int lower = basePort;
102+
final int range = 0xFFFF;
103+
104+
// Select a start point in the range
105+
final int initialOffset = RANDOM.nextInt(range - lower);
106+
107+
// Loop the offset through all ports in the range and attempt
108+
// to bind to each
109+
int offset = initialOffset;
110+
ServerSocket socket;
111+
do {
112+
final int port = lower + offset;
113+
try {
114+
socket = new ServerSocket(port);
115+
socket.close();
116+
return port;
117+
} catch (IOException caught) {
118+
// Swallow exceptions until the end
119+
}
120+
offset = (offset + 1) % range;
121+
} while (offset != initialOffset);
122+
123+
// If a port can't be bound, throw the exception
124+
throw new IllegalArgumentException(LocalizationMessages.COULD_NOT_BIND_TO_ANY_PORT());
125+
}
126+
74127
/**
75128
* Return {@link SSLContext} in the configuration if the protocol scheme is {@code HTTPS}.
76129
* @return the SSLContext in the configuration.
@@ -100,6 +153,16 @@ public boolean autoStart() {
100153
return autoStart;
101154
}
102155

156+
/**
157+
* Defines if the {@link WebServer} should start on a privileged port when port is not set.
158+
* @return true if {@link ServerProperties#WEBSERVER_AUTO_START} is {@code true}, {@code false} otherwise.
159+
*/
160+
public boolean allowPrivilegedPorts() {
161+
return Optional.ofNullable(
162+
(Boolean) configuration.property(ServerProperties.WEBSERVER_ALLOW_PRIVILEGED_PORTS))
163+
.orElse(FALSE);
164+
}
165+
103166
/**
104167
* Factory method creating {@code JerseySeBootstrapConfiguration} wrapper around {@link SeBootstrap.Configuration}.
105168
* @param configuration wrapped configuration
@@ -129,16 +192,17 @@ public static final class Builder implements SeBootstrap.Configuration.Builder {
129192
PROPERTY_TYPES.put(SeBootstrap.Configuration.ROOT_PATH, String.class);
130193
PROPERTY_TYPES.put(SeBootstrap.Configuration.SSL_CONTEXT, SSLContext.class);
131194
PROPERTY_TYPES.put(SeBootstrap.Configuration.SSL_CLIENT_AUTHENTICATION, SSLClientAuthentication.class);
132-
PROPERTY_TYPES.put(ServerProperties.WEBSERVER_CLASS, Class.class);
195+
PROPERTY_TYPES.put(ServerProperties.WEBSERVER_ALLOW_PRIVILEGED_PORTS, Boolean.class);
133196
PROPERTY_TYPES.put(ServerProperties.WEBSERVER_AUTO_START, Boolean.class);
197+
PROPERTY_TYPES.put(ServerProperties.WEBSERVER_CLASS, Class.class);
134198
}
135199

136200
private final Map<String, Object> properties = new HashMap<>();
137201

138202
private Builder() {
139203
this.properties.put(SeBootstrap.Configuration.PROTOCOL, "HTTP"); // upper case mandated by javadoc
140204
this.properties.put(SeBootstrap.Configuration.HOST, "localhost");
141-
this.properties.put(SeBootstrap.Configuration.PORT, -1); // Auto-select port 80 for HTTP or 443 for HTTPS
205+
this.properties.put(SeBootstrap.Configuration.PORT, -1); // Auto-select port 8080 for HTTP or 8443 for HTTPS
142206
this.properties.put(SeBootstrap.Configuration.ROOT_PATH, "/");
143207
this.properties.put(ServerProperties.WEBSERVER_CLASS, WebServer.class); // Auto-select first provider
144208
try {
@@ -149,6 +213,15 @@ private Builder() {
149213
this.properties.put(SeBootstrap.Configuration.SSL_CLIENT_AUTHENTICATION,
150214
SeBootstrap.Configuration.SSLClientAuthentication.NONE);
151215
this.properties.put(ServerProperties.WEBSERVER_AUTO_START, TRUE);
216+
this.properties.put(ServerProperties.WEBSERVER_ALLOW_PRIVILEGED_PORTS, FALSE);
217+
218+
SystemPropertiesConfigurationModel propertiesConfigurationModel = new SystemPropertiesConfigurationModel(
219+
Collections.singletonList(Properties.class.getName())
220+
);
221+
from((name, aClass) -> String.class.equals(aClass) || Integer.class.equals(aClass) || Boolean.class.equals(aClass)
222+
? propertiesConfigurationModel.getOptionalProperty(name, aClass)
223+
: Optional.empty()
224+
);
152225
}
153226

154227
@Override
@@ -208,4 +281,41 @@ public JerseySeBootstrapConfiguration.Builder from(Object externalConfig) {
208281
return this;
209282
}
210283
}
284+
285+
/**
286+
* Name the properties to be internally read from System properties by {@link ExternalPropertiesConfigurationFactory}.
287+
* This is required just when SecurityManager is on, otherwise all system properties are read.
288+
*/
289+
@PropertiesClass
290+
private static class Properties {
291+
/**
292+
* See {@link SeBootstrap.Configuration#PROTOCOL} property.
293+
*/
294+
public static final String SE_BOOTSTRAP_CONFIGURATION_PROTOCOL = SeBootstrap.Configuration.PROTOCOL;
295+
296+
/**
297+
* See {@link SeBootstrap.Configuration#HOST} property.
298+
*/
299+
public static final String SE_BOOTSTRAP_CONFIGURATION_HOST = SeBootstrap.Configuration.HOST;
300+
301+
/**
302+
* See {@link SeBootstrap.Configuration#PORT} property.
303+
*/
304+
public static final String SE_BOOTSTRAP_CONFIGURATION_PORT = SeBootstrap.Configuration.PORT;
305+
306+
/**
307+
* See {@link SeBootstrap.Configuration#ROOT_PATH} property.
308+
*/
309+
public static final String SE_BOOTSTRAP_CONFIGURATION_ROOT_PATH = SeBootstrap.Configuration.ROOT_PATH;
310+
311+
/**
312+
* See {@link ServerProperties#WEBSERVER_ALLOW_PRIVILEGED_PORTS} property.
313+
*/
314+
public static final String WEBSERVER_ALLOW_PRIVILEGED_PORTS = ServerProperties.WEBSERVER_ALLOW_PRIVILEGED_PORTS;
315+
316+
/**
317+
* See {@link ServerProperties#WEBSERVER_AUTO_START} property.
318+
*/
319+
public static final String WEBSERVER_AUTO_START = ServerProperties.WEBSERVER_AUTO_START;
320+
}
211321
}

core-server/src/main/java/org/glassfish/jersey/server/ServerProperties.java

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -24,6 +24,7 @@
2424
import org.glassfish.jersey.internal.util.PropertiesClass;
2525
import org.glassfish.jersey.internal.util.PropertiesHelper;
2626
import org.glassfish.jersey.internal.util.PropertyAlias;
27+
import org.glassfish.jersey.server.spi.Container;
2728
import org.glassfish.jersey.server.spi.WebServer;
2829

2930

@@ -38,25 +39,6 @@
3839
@PropertiesClass
3940
public final class ServerProperties {
4041

41-
/**
42-
* Defines the implementation of {@link WebServer} to bootstrap.
43-
* <p>
44-
* By default auto-selects the first server provider found.
45-
* </p>
46-
* @since 3.1.0
47-
*/
48-
public static final String WEBSERVER_CLASS = "jersey.config.server.webserver.class";
49-
50-
/**
51-
* Whether to automatically startup {@link WebServer} at bootstrap.
52-
* <p>
53-
* By default, servers are immediately listening to connections after bootstrap,
54-
* so no explicit invocation of {@link WebServer#start()} is needed.
55-
* </p>
56-
* @since 3.1.0
57-
*/
58-
public static final String WEBSERVER_AUTO_START = "jersey.config.server.webserver.autostart";
59-
6042
/**
6143
* Defines one or more packages that contain application-specific resources and
6244
* providers.
@@ -783,6 +765,50 @@ public final class ServerProperties {
783765
public static final String EMPTY_REQUEST_MEDIA_TYPE_MATCHES_ANY_CONSUMES =
784766
"jersey.config.server.empty.request.media.matches.any.consumes";
785767

768+
/**
769+
* Defines whether to allow privileged ports (0-1023) to be used to start the {@link WebServer} implementation
770+
* to be chosen from the unused ports when the {@link jakarta.ws.rs.SeBootstrap.Configuration#PORT} is set to {@code -1}
771+
* or unset.
772+
* <p>
773+
* The default ports are {@link Container#DEFAULT_HTTP_PORT} for HTTP and {@link Container#DEFAULT_HTTPS_PORT}
774+
* for HTTPS when {@code WEBSERVER_ALLOW_PRIVILEGED_PORTS} is {@code true} or 8080 for HTTP and 8443 for HTTPS when
775+
* {@code WEBSERVER_ALLOW_PRIVILEGED_PORTS} is {@code false}.
776+
* </p>
777+
* <p>
778+
* If {@link jakarta.ws.rs.SeBootstrap.Configuration#PORT} is set to {@code 0}, the implementation chooses random ports
779+
* (0-65535) when {@code WEBSERVER_ALLOW_PRIVILEGED_PORTS} is {@code true}, or (1024-65535) when
780+
* {@code WEBSERVER_ALLOW_PRIVILEGED_PORTS} is {@code false.}
781+
* </p>
782+
* <p>
783+
* The default this is {@code false}. Use {@code true} to allow a restricted port number. The name of the configuration
784+
* property is <tt>{@value}</tt>.
785+
* </p>
786+
* @since 3.1.0
787+
*/
788+
public static final String WEBSERVER_ALLOW_PRIVILEGED_PORTS =
789+
"jersey.config.server.bootstrap.webserver.allow.privileged.ports";
790+
791+
/**
792+
* Whether to automatically startup {@link WebServer} at bootstrap.
793+
* <p>
794+
* By default, servers are immediately listening to connections after bootstrap,
795+
* so no explicit invocation of {@link WebServer#start()} is needed. The name of the configuration
796+
* property is <tt>{@value}</tt>.
797+
* </p>
798+
* @since 3.1.0
799+
*/
800+
public static final String WEBSERVER_AUTO_START = "jersey.config.server.bootstrap.webserver.autostart";
801+
802+
/**
803+
* Defines the implementation of {@link WebServer} to bootstrap.
804+
* <p>
805+
* By default auto-selects the first server provider found. The name of the configuration
806+
* property is <tt>{@value}</tt>.
807+
* </p>
808+
* @since 3.1.0
809+
*/
810+
public static final String WEBSERVER_CLASS = "jersey.config.server.bootstrap.webserver.class";
811+
786812
/**
787813
* JVM argument to define the value of
788814
* {@link org.glassfish.jersey.server.internal.monitoring.core.ReservoirConstants#COLLISION_BUFFER_POWER}.

0 commit comments

Comments
 (0)