Skip to content

Commit fdffdd1

Browse files
authored
[8.x] [Entitlements] Network access checks for miscellanea (#120262) (#120353)
* [Entitlements] Network access checks for miscellanea (#120262) * Move checks that use version-specific API
1 parent 4aa346d commit fdffdd1

File tree

11 files changed

+247
-3
lines changed

11 files changed

+247
-3
lines changed

distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/SystemJvmOptions.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,8 @@ private static Stream<String> maybeAttachEntitlementAgent(boolean useEntitlement
180180
throw new IllegalStateException("Failed to list entitlement jars in: " + dir, e);
181181
}
182182
// We instrument classes in these modules to call the bridge. Because the bridge gets patched
183-
// into java.base, we must export the bridge from java.base to these modules.
184-
String modulesContainingEntitlementInstrumentation = "java.logging";
183+
// into java.base, we must export the bridge from java.base to these modules, as a comma-separated list
184+
String modulesContainingEntitlementInstrumentation = "java.logging,java.net.http,java.naming";
185185
return Stream.of(
186186
"-Des.entitlements.enabled=true",
187187
"-XX:+EnableDynamicAgentLoading",

libs/entitlement/bridge/src/main/java/module-info.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,7 @@
1010
// This module-info is used just to satisfy your IDE.
1111
// At build and run time, the bridge is patched into the java.base module.
1212
module org.elasticsearch.entitlement.bridge {
13+
requires java.net.http;
14+
1315
exports org.elasticsearch.entitlement.bridge;
1416
}

libs/entitlement/bridge/src/main/java/org/elasticsearch/entitlement/bridge/EntitlementChecker.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030
import java.net.URL;
3131
import java.net.URLStreamHandler;
3232
import java.net.URLStreamHandlerFactory;
33+
import java.net.http.HttpClient;
34+
import java.net.http.HttpRequest;
35+
import java.net.http.HttpResponse;
36+
import java.security.cert.CertStoreParameters;
3337
import java.util.List;
3438

3539
import javax.net.ssl.HostnameVerifier;
@@ -254,4 +258,37 @@ public interface EntitlementChecker {
254258
void check$java_net_Socket$connect(Class<?> callerClass, Socket that, SocketAddress endpoint);
255259

256260
void check$java_net_Socket$connect(Class<?> callerClass, Socket that, SocketAddress endpoint, int backlog);
261+
262+
// Network miscellanea
263+
void check$java_net_URL$openConnection(Class<?> callerClass, java.net.URL that, Proxy proxy);
264+
265+
// HttpClient.Builder is an interface, so we instrument its only (internal) implementation
266+
void check$jdk_internal_net_http_HttpClientBuilderImpl$build(Class<?> callerClass, HttpClient.Builder that);
267+
268+
// HttpClient#send and sendAsync are abstract, so we instrument their internal implementation
269+
void check$jdk_internal_net_http_HttpClientImpl$send(
270+
Class<?> callerClass,
271+
HttpClient that,
272+
HttpRequest request,
273+
HttpResponse.BodyHandler<?> responseBodyHandler
274+
);
275+
276+
void check$jdk_internal_net_http_HttpClientImpl$sendAsync(
277+
Class<?> callerClass,
278+
HttpClient that,
279+
HttpRequest userRequest,
280+
HttpResponse.BodyHandler<?> responseHandler
281+
);
282+
283+
void check$jdk_internal_net_http_HttpClientImpl$sendAsync(
284+
Class<?> callerClass,
285+
HttpClient that,
286+
HttpRequest userRequest,
287+
HttpResponse.BodyHandler<?> responseHandler,
288+
HttpResponse.PushPromiseHandler<?> pushPromiseHandler
289+
);
290+
291+
// We need to check the LDAPCertStore, as this will connect, but this is internal/created via SPI,
292+
// so we instrument the general factory instead and then filter in the check method implementation
293+
void check$java_security_cert_CertStore$$getInstance(Class<?> callerClass, String type, CertStoreParameters params);
257294
}

libs/entitlement/qa/common/src/main/java/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
// Modules we'll attempt to use in order to exercise entitlements
1616
requires java.logging;
17+
requires java.net.http;
1718

1819
exports org.elasticsearch.entitlement.qa.common;
1920
}

libs/entitlement/qa/common/src/main/java/org/elasticsearch/entitlement/qa/common/NetworkAccessCheckActions.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@
1717
import java.net.Proxy;
1818
import java.net.ServerSocket;
1919
import java.net.Socket;
20+
import java.net.URI;
21+
import java.net.URISyntaxException;
22+
import java.security.InvalidAlgorithmParameterException;
23+
import java.security.NoSuchAlgorithmException;
24+
import java.security.cert.CertStore;
25+
import java.util.Arrays;
2026

2127
class NetworkAccessCheckActions {
2228

@@ -59,4 +65,21 @@ static void socketConnect() throws IOException {
5965
socket.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));
6066
}
6167
}
68+
69+
@SuppressForbidden(reason = "Testing entitlement check on forbidden action")
70+
static void urlOpenConnectionWithProxy() throws URISyntaxException, IOException {
71+
var url = new URI("http://localhost").toURL();
72+
var urlConnection = url.openConnection(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(0)));
73+
assert urlConnection != null;
74+
}
75+
76+
static void createLDAPCertStore() throws NoSuchAlgorithmException {
77+
try {
78+
// We pass down null params to provoke a InvalidAlgorithmParameterException
79+
CertStore.getInstance("LDAP", null);
80+
} catch (InvalidAlgorithmParameterException ex) {
81+
// Assert we actually hit the class we care about, LDAPCertStore (or its impl)
82+
assert Arrays.stream(ex.getStackTrace()).anyMatch(e -> e.getClassName().endsWith("LDAPCertStore"));
83+
}
84+
}
6285
}

libs/entitlement/qa/common/src/main/java/org/elasticsearch/entitlement/qa/common/RestEntitlementsCheckAction.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,13 @@ static CheckAction alwaysDenied(CheckedRunnable<Exception> action) {
157157
entry("socket_bind", forPlugins(NetworkAccessCheckActions::socketBind)),
158158
entry("socket_connect", forPlugins(NetworkAccessCheckActions::socketConnect)),
159159
entry("server_socket_bind", forPlugins(NetworkAccessCheckActions::serverSocketBind)),
160-
entry("server_socket_accept", forPlugins(NetworkAccessCheckActions::serverSocketAccept))
160+
entry("server_socket_accept", forPlugins(NetworkAccessCheckActions::serverSocketAccept)),
161+
162+
entry("url_open_connection_proxy", forPlugins(NetworkAccessCheckActions::urlOpenConnectionWithProxy)),
163+
entry("http_client_builder_build", forPlugins(VersionSpecificNetworkChecks::httpClientBuilderBuild)),
164+
entry("http_client_send", forPlugins(VersionSpecificNetworkChecks::httpClientSend)),
165+
entry("http_client_send_async", forPlugins(VersionSpecificNetworkChecks::httpClientSendAsync)),
166+
entry("create_ldap_cert_store", forPlugins(NetworkAccessCheckActions::createLDAPCertStore))
161167
)
162168
.filter(entry -> entry.getValue().fromJavaVersion() == null || Runtime.version().feature() >= entry.getValue().fromJavaVersion())
163169
.collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue));

libs/entitlement/qa/common/src/main/java/org/elasticsearch/entitlement/qa/common/VersionSpecificNetworkChecks.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,30 @@
99

1010
package org.elasticsearch.entitlement.qa.common;
1111

12+
import java.io.IOException;
13+
import java.net.URI;
14+
import java.net.http.HttpClient;
15+
import java.net.http.HttpRequest;
16+
import java.net.http.HttpResponse;
17+
1218
class VersionSpecificNetworkChecks {
1319
static void createInetAddressResolverProvider() {}
20+
21+
static void httpClientBuilderBuild() {
22+
HttpClient.newBuilder().build();
23+
}
24+
25+
static void httpClientSend() throws InterruptedException {
26+
HttpClient httpClient = HttpClient.newBuilder().build();
27+
try {
28+
httpClient.send(HttpRequest.newBuilder(URI.create("http://localhost")).build(), HttpResponse.BodyHandlers.discarding());
29+
} catch (IOException e) {
30+
// Expected, the send action may fail with these parameters (but after it run the entitlement check in the prologue)
31+
}
32+
}
33+
34+
static void httpClientSendAsync() {
35+
HttpClient httpClient = HttpClient.newBuilder().build();
36+
httpClient.sendAsync(HttpRequest.newBuilder(URI.create("http://localhost")).build(), HttpResponse.BodyHandlers.discarding());
37+
}
1438
}

libs/entitlement/qa/common/src/main18/java/org/elasticsearch/entitlement/qa/common/VersionSpecificNetworkChecks.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99

1010
package org.elasticsearch.entitlement.qa.common;
1111

12+
import java.io.IOException;
13+
import java.net.URI;
14+
import java.net.http.HttpClient;
15+
import java.net.http.HttpRequest;
16+
import java.net.http.HttpResponse;
1217
import java.net.spi.InetAddressResolver;
1318
import java.net.spi.InetAddressResolverProvider;
1419

@@ -26,4 +31,22 @@ public String name() {
2631
}
2732
};
2833
}
34+
35+
static void httpClientBuilderBuild() {
36+
HttpClient.newBuilder().build();
37+
}
38+
39+
static void httpClientSend() throws InterruptedException {
40+
HttpClient httpClient = HttpClient.newBuilder().build();
41+
try {
42+
httpClient.send(HttpRequest.newBuilder(URI.create("http://localhost")).build(), HttpResponse.BodyHandlers.discarding());
43+
} catch (IOException e) {
44+
// Expected, the send action may fail with these parameters (but after it run the entitlement check in the prologue)
45+
}
46+
}
47+
48+
static void httpClientSendAsync() {
49+
HttpClient httpClient = HttpClient.newBuilder().build();
50+
httpClient.sendAsync(HttpRequest.newBuilder(URI.create("http://localhost")).build(), HttpResponse.BodyHandlers.discarding());
51+
}
2952
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.entitlement.qa.common;
11+
12+
import java.io.IOException;
13+
import java.net.URI;
14+
import java.net.http.HttpClient;
15+
import java.net.http.HttpRequest;
16+
import java.net.http.HttpResponse;
17+
import java.net.spi.InetAddressResolver;
18+
import java.net.spi.InetAddressResolverProvider;
19+
20+
class VersionSpecificNetworkChecks {
21+
static void createInetAddressResolverProvider() {
22+
var x = new InetAddressResolverProvider() {
23+
@Override
24+
public InetAddressResolver get(Configuration configuration) {
25+
return null;
26+
}
27+
28+
@Override
29+
public String name() {
30+
return "TEST";
31+
}
32+
};
33+
}
34+
35+
static void httpClientBuilderBuild() {
36+
try (HttpClient httpClient = HttpClient.newBuilder().build()) {
37+
assert httpClient != null;
38+
}
39+
}
40+
41+
static void httpClientSend() throws InterruptedException {
42+
try (HttpClient httpClient = HttpClient.newBuilder().build()) {
43+
// Shutdown the client, so the send action will shortcut before actually executing any network operation
44+
// (but after it run our check in the prologue)
45+
httpClient.shutdown();
46+
try {
47+
httpClient.send(HttpRequest.newBuilder(URI.create("http://localhost")).build(), HttpResponse.BodyHandlers.discarding());
48+
} catch (IOException e) {
49+
// Expected, since we shut down the client
50+
}
51+
}
52+
}
53+
54+
static void httpClientSendAsync() {
55+
try (HttpClient httpClient = HttpClient.newBuilder().build()) {
56+
// Shutdown the client, so the send action will return before actually executing any network operation
57+
// (but after it run our check in the prologue)
58+
httpClient.shutdown();
59+
var future = httpClient.sendAsync(
60+
HttpRequest.newBuilder(URI.create("http://localhost")).build(),
61+
HttpResponse.BodyHandlers.discarding()
62+
);
63+
assert future.isCompletedExceptionally();
64+
future.exceptionally(ex -> {
65+
assert ex instanceof IOException;
66+
return null;
67+
});
68+
}
69+
}
70+
}

libs/entitlement/src/main/java/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
requires java.instrument;
1414
requires org.elasticsearch.base;
1515
requires jdk.attach;
16+
requires java.net.http;
1617

1718
requires static org.elasticsearch.entitlement.bridge; // At runtime, this will be in java.base
1819

0 commit comments

Comments
 (0)