Skip to content

Commit f8aa047

Browse files
authored
Entitlements: manage_threads (#122261)
* Refactor: protected -> private * Initial thread-related entitlements * Entitlements from manual test runs * Refactor: notEntitled method * Entitlements reporting mode * Entitlements from CI * Revert "Entitlements reporting mode" This reverts commit 443ca76. * Remove unnecessary EntitledActions.newThread * Don't log in entitlements ITs by default * Import SuppressForbidden * Respond to PR comments * Move manage_threads tests to their own file
1 parent 6ff8bed commit f8aa047

File tree

23 files changed

+256
-47
lines changed

23 files changed

+256
-47
lines changed

libs/entitlement/asm-provider/src/main/java/org/elasticsearch/entitlement/instrumentation/impl/InstrumenterImpl.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,14 +152,13 @@ public MethodVisitor visitMethod(int access, String name, String descriptor, Str
152152
if (isAnnotationPresent == false) {
153153
boolean isStatic = (access & ACC_STATIC) != 0;
154154
boolean isCtor = "<init>".equals(name);
155-
boolean hasReceiver = (isStatic || isCtor) == false;
156155
var key = new MethodKey(className, name, Stream.of(Type.getArgumentTypes(descriptor)).map(Type::getInternalName).toList());
157156
var instrumentationMethod = checkMethods.get(key);
158157
if (instrumentationMethod != null) {
159-
// LOGGER.debug("Will instrument method {}", key);
158+
// System.out.println("Will instrument method " + key);
160159
return new EntitlementMethodVisitor(Opcodes.ASM9, mv, isStatic, isCtor, descriptor, instrumentationMethod);
161160
} else {
162-
// LOGGER.trace("Will not instrument method {}", key);
161+
// System.out.println("Will not instrument method " + key);
163162
}
164163
}
165164
return mv;

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
import java.util.Set;
7070
import java.util.TimeZone;
7171
import java.util.concurrent.ExecutorService;
72+
import java.util.concurrent.ForkJoinPool;
7273
import java.util.function.Consumer;
7374

7475
import javax.net.ssl.HostnameVerifier;
@@ -652,4 +653,26 @@ void checkNewByteChannel(
652653
void checkName(Class<?> callerClass, FileStore that);
653654

654655
void checkType(Class<?> callerClass, FileStore that);
656+
657+
////////////////////
658+
//
659+
// Thread management
660+
//
661+
662+
void check$java_lang_Thread$start(Class<?> callerClass, Thread thread);
663+
664+
void check$java_lang_Thread$setDaemon(Class<?> callerClass, Thread thread, boolean on);
665+
666+
void check$java_lang_ThreadGroup$setDaemon(Class<?> callerClass, ThreadGroup threadGroup, boolean daemon);
667+
668+
void check$java_util_concurrent_ForkJoinPool$setParallelism(Class<?> callerClass, ForkJoinPool forkJoinPool, int size);
669+
670+
void check$java_lang_Thread$setName(Class<?> callerClass, Thread thread, String name);
671+
672+
void check$java_lang_Thread$setPriority(Class<?> callerClass, Thread thread, int newPriority);
673+
674+
void check$java_lang_Thread$setUncaughtExceptionHandler(Class<?> callerClass, Thread thread, Thread.UncaughtExceptionHandler ueh);
675+
676+
void check$java_lang_ThreadGroup$setMaxPriority(Class<?> callerClass, ThreadGroup threadGroup, int pri);
677+
655678
}

libs/entitlement/qa/entitled-plugin/src/main/java/org/elasticsearch/entitlement/qa/entitled/EntitledActions.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,6 @@ private static Path readWriteDir() {
3434
return testRootDir.resolve("read_write_dir");
3535
}
3636

37-
static void System_clearProperty(String key) {
38-
System.clearProperty(key);
39-
}
40-
4137
public static UserPrincipal getFileOwner(Path path) throws IOException {
4238
return Files.getOwner(path);
4339
}

libs/entitlement/qa/entitled-plugin/src/main/java/org/elasticsearch/entitlement/qa/entitled/EntitledPlugin.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import org.elasticsearch.plugins.ExtensiblePlugin;
1616
import org.elasticsearch.plugins.Plugin;
1717

18-
import static org.elasticsearch.entitlement.qa.entitled.EntitledActions.System_clearProperty;
18+
import java.util.concurrent.atomic.AtomicBoolean;
1919

2020
public class EntitledPlugin extends Plugin implements ExtensiblePlugin {
2121

@@ -28,11 +28,19 @@ public static void selfTest() {
2828
selfTestNotEntitled();
2929
}
3030

31-
private static final String SELF_TEST_PROPERTY = "org.elasticsearch.entitlement.qa.selfTest";
32-
3331
private static void selfTestEntitled() {
3432
logger.debug("selfTestEntitled");
35-
System_clearProperty(SELF_TEST_PROPERTY);
33+
AtomicBoolean threadRan = new AtomicBoolean(false);
34+
try {
35+
Thread testThread = new Thread(() -> threadRan.set(true), "testThread");
36+
testThread.start();
37+
testThread.join();
38+
} catch (InterruptedException e) {
39+
throw new AssertionError(e);
40+
}
41+
if (threadRan.get() == false) {
42+
throw new AssertionError("Self-test thread did not run");
43+
}
3644
}
3745

3846
private static void selfTestNotEntitled() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
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.test;
11+
12+
import org.elasticsearch.core.SuppressForbidden;
13+
14+
import java.util.concurrent.ForkJoinPool;
15+
import java.util.concurrent.atomic.AtomicBoolean;
16+
17+
import static java.lang.Thread.currentThread;
18+
import static org.elasticsearch.entitlement.qa.test.EntitlementTest.ExpectedAccess.PLUGINS;
19+
20+
@SuppressForbidden(reason = "testing entitlements")
21+
@SuppressWarnings("unused") // used via reflection
22+
class ManageThreadsActions {
23+
private ManageThreadsActions() {}
24+
25+
@EntitlementTest(expectedAccess = PLUGINS)
26+
static void java_lang_Thread$start() throws InterruptedException {
27+
AtomicBoolean threadRan = new AtomicBoolean(false);
28+
Thread thread = new Thread(() -> threadRan.set(true), "test");
29+
thread.start();
30+
thread.join();
31+
assert threadRan.get();
32+
}
33+
34+
@EntitlementTest(expectedAccess = PLUGINS)
35+
static void java_lang_Thread$setDaemon() {
36+
new Thread().setDaemon(true);
37+
}
38+
39+
@EntitlementTest(expectedAccess = PLUGINS)
40+
static void java_lang_ThreadGroup$setDaemon() {
41+
currentThread().getThreadGroup().setDaemon(currentThread().getThreadGroup().isDaemon());
42+
}
43+
44+
@EntitlementTest(expectedAccess = PLUGINS)
45+
static void java_util_concurrent_ForkJoinPool$setParallelism() {
46+
ForkJoinPool.commonPool().setParallelism(ForkJoinPool.commonPool().getParallelism());
47+
}
48+
49+
@EntitlementTest(expectedAccess = PLUGINS)
50+
static void java_lang_Thread$setName() {
51+
currentThread().setName(currentThread().getName());
52+
}
53+
54+
@EntitlementTest(expectedAccess = PLUGINS)
55+
static void java_lang_Thread$setPriority() {
56+
currentThread().setPriority(currentThread().getPriority());
57+
}
58+
59+
@EntitlementTest(expectedAccess = PLUGINS)
60+
static void java_lang_Thread$setUncaughtExceptionHandler() {
61+
currentThread().setUncaughtExceptionHandler(currentThread().getUncaughtExceptionHandler());
62+
}
63+
64+
@EntitlementTest(expectedAccess = PLUGINS)
65+
static void java_lang_ThreadGroup$setMaxPriority() {
66+
currentThread().getThreadGroup().setMaxPriority(currentThread().getThreadGroup().getMaxPriority());
67+
}
68+
69+
}

libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/RestEntitlementsCheckAction.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,13 +181,17 @@ static CheckAction alwaysDenied(CheckedRunnable<Exception> action) {
181181
entry("runtime_load_library", forPlugins(LoadNativeLibrariesCheckActions::runtimeLoadLibrary)),
182182
entry("system_load", forPlugins(LoadNativeLibrariesCheckActions::systemLoad)),
183183
entry("system_load_library", forPlugins(LoadNativeLibrariesCheckActions::systemLoadLibrary))
184+
185+
// MAINTENANCE NOTE: Please don't add any more entries to this map.
186+
// Put new tests into their own "Actions" class using the @EntitlementTest annotation.
184187
),
185188
getTestEntries(FileCheckActions.class),
186-
getTestEntries(SpiActions.class),
187-
getTestEntries(SystemActions.class),
189+
getTestEntries(FileStoreActions.class),
190+
getTestEntries(ManageThreadsActions.class),
188191
getTestEntries(NativeActions.class),
189192
getTestEntries(NioFileSystemActions.class),
190-
getTestEntries(FileStoreActions.class)
193+
getTestEntries(SpiActions.class),
194+
getTestEntries(SystemActions.class)
191195
)
192196
.flatMap(Function.identity())
193197
.filter(entry -> entry.getValue().fromJavaVersion() == null || Runtime.version().feature() >= entry.getValue().fromJavaVersion())
@@ -424,7 +428,9 @@ protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient cli
424428
return channel -> {
425429
logger.info("Calling check action [{}]", actionName);
426430
checkAction.action().run();
431+
logger.debug("Check action [{}] returned", actionName);
427432
channel.sendResponse(new RestResponse(RestStatus.OK, Strings.format("Succesfully executed action [%s]", actionName)));
428433
};
429434
}
435+
430436
}

libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/AbstractEntitlementsIT.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public abstract class AbstractEntitlementsIT extends ESRestTestCase {
2929
builder.value("inbound_network");
3030
builder.value("outbound_network");
3131
builder.value("load_native_libraries");
32+
builder.value("manage_threads");
3233
builder.value(
3334
Map.of(
3435
"write_system_properties",

libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsTestRule.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class EntitlementsTestRule implements TestRule {
3333

3434
// entitlements that test methods may use, see EntitledActions
3535
private static final PolicyBuilder ENTITLED_POLICY = (builder, tempDir) -> {
36-
builder.value(Map.of("write_system_properties", Map.of("properties", List.of("org.elasticsearch.entitlement.qa.selfTest"))));
36+
builder.value("manage_threads");
3737
builder.value(
3838
Map.of(
3939
"files",
@@ -74,6 +74,8 @@ protected void before() throws Throwable {
7474
.systemProperty("es.entitlements.enabled", "true")
7575
.systemProperty("es.entitlements.testdir", () -> testDir.getRoot().getAbsolutePath())
7676
.setting("xpack.security.enabled", "false")
77+
// Logs in libs/entitlement/qa/build/test-results/javaRestTest/TEST-org.elasticsearch.entitlement.qa.EntitlementsXXX.xml
78+
// .setting("logger.org.elasticsearch.entitlement", "DEBUG")
7779
.build();
7880
ruleChain = RuleChain.outerRule(testDir).around(tempDirSetup).around(cluster);
7981
}

libs/entitlement/src/main/java/org/elasticsearch/entitlement/initialization/EntitlementInitialization.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.elasticsearch.entitlement.runtime.policy.entitlements.FilesEntitlement.FileData;
2929
import org.elasticsearch.entitlement.runtime.policy.entitlements.InboundNetworkEntitlement;
3030
import org.elasticsearch.entitlement.runtime.policy.entitlements.LoadNativeLibrariesEntitlement;
31+
import org.elasticsearch.entitlement.runtime.policy.entitlements.ManageThreadsEntitlement;
3132
import org.elasticsearch.entitlement.runtime.policy.entitlements.OutboundNetworkEntitlement;
3233
import org.elasticsearch.entitlement.runtime.policy.entitlements.ReadStoreAttributesEntitlement;
3334

@@ -143,14 +144,16 @@ private static PolicyManager createPolicyManager() {
143144
new InboundNetworkEntitlement(),
144145
new OutboundNetworkEntitlement(),
145146
new LoadNativeLibrariesEntitlement(),
147+
new ManageThreadsEntitlement(),
146148
new FilesEntitlement(
147149
List.of(new FilesEntitlement.FileData(EntitlementBootstrap.bootstrapArgs().tempDir().toString(), READ_WRITE))
148150
)
149151
)
150152
),
151153
new Scope("org.apache.httpcomponents.httpclient", List.of(new OutboundNetworkEntitlement())),
152154
new Scope("io.netty.transport", List.of(new InboundNetworkEntitlement(), new OutboundNetworkEntitlement())),
153-
new Scope("org.apache.lucene.core", List.of(new LoadNativeLibrariesEntitlement())),
155+
new Scope("org.apache.lucene.core", List.of(new LoadNativeLibrariesEntitlement(), new ManageThreadsEntitlement())),
156+
new Scope("org.apache.logging.log4j.core", List.of(new ManageThreadsEntitlement())),
154157
new Scope(
155158
"org.elasticsearch.nativeaccess",
156159
List.of(
@@ -162,7 +165,7 @@ private static PolicyManager createPolicyManager() {
162165
);
163166
// agents run without a module, so this is a special hack for the apm agent
164167
// this should be removed once https://github.com/elastic/elasticsearch/issues/109335 is completed
165-
List<Entitlement> agentEntitlements = List.of(new CreateClassLoaderEntitlement());
168+
List<Entitlement> agentEntitlements = List.of(new CreateClassLoaderEntitlement(), new ManageThreadsEntitlement());
166169
var resolver = EntitlementBootstrap.bootstrapArgs().pluginResolver();
167170
return new PolicyManager(serverPolicy, agentEntitlements, pluginPolicies, resolver, AGENTS_PACKAGE_NAME, ENTITLEMENTS_MODULE);
168171
}

libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/api/ElasticsearchEntitlementChecker.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
import java.util.Set;
7676
import java.util.TimeZone;
7777
import java.util.concurrent.ExecutorService;
78+
import java.util.concurrent.ForkJoinPool;
7879
import java.util.function.Consumer;
7980

8081
import javax.net.ssl.HostnameVerifier;
@@ -1278,6 +1279,52 @@ public void checkExists(Class<?> callerClass, FileSystemProvider that, Path path
12781279
policyManager.checkFileRead(callerClass, path);
12791280
}
12801281

1282+
// Thread management
1283+
1284+
@Override
1285+
public void check$java_lang_Thread$start(Class<?> callerClass, Thread thread) {
1286+
policyManager.checkManageThreadsEntitlement(callerClass);
1287+
}
1288+
1289+
@Override
1290+
public void check$java_lang_Thread$setDaemon(Class<?> callerClass, Thread thread, boolean on) {
1291+
policyManager.checkManageThreadsEntitlement(callerClass);
1292+
}
1293+
1294+
@Override
1295+
public void check$java_lang_ThreadGroup$setDaemon(Class<?> callerClass, ThreadGroup threadGroup, boolean daemon) {
1296+
policyManager.checkManageThreadsEntitlement(callerClass);
1297+
}
1298+
1299+
@Override
1300+
public void check$java_util_concurrent_ForkJoinPool$setParallelism(Class<?> callerClass, ForkJoinPool forkJoinPool, int size) {
1301+
policyManager.checkManageThreadsEntitlement(callerClass);
1302+
}
1303+
1304+
@Override
1305+
public void check$java_lang_Thread$setName(Class<?> callerClass, Thread thread, String name) {
1306+
policyManager.checkManageThreadsEntitlement(callerClass);
1307+
}
1308+
1309+
@Override
1310+
public void check$java_lang_Thread$setPriority(Class<?> callerClass, Thread thread, int newPriority) {
1311+
policyManager.checkManageThreadsEntitlement(callerClass);
1312+
}
1313+
1314+
@Override
1315+
public void check$java_lang_Thread$setUncaughtExceptionHandler(
1316+
Class<?> callerClass,
1317+
Thread thread,
1318+
Thread.UncaughtExceptionHandler ueh
1319+
) {
1320+
policyManager.checkManageThreadsEntitlement(callerClass);
1321+
}
1322+
1323+
@Override
1324+
public void check$java_lang_ThreadGroup$setMaxPriority(Class<?> callerClass, ThreadGroup threadGroup, int pri) {
1325+
policyManager.checkManageThreadsEntitlement(callerClass);
1326+
}
1327+
12811328
@Override
12821329
public void checkGetFileStoreAttributeView(Class<?> callerClass, FileStore that, Class<?> type) {
12831330
policyManager.checkWriteStoreAttributes(callerClass);

0 commit comments

Comments
 (0)