Skip to content

Commit 443513f

Browse files
committed
Entitlements: manage_threads (elastic#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 cc78f18 commit 443513f

File tree

23 files changed

+258
-48
lines changed

23 files changed

+258
-48
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
@@ -62,6 +62,7 @@
6262
import java.util.Set;
6363
import java.util.TimeZone;
6464
import java.util.concurrent.ExecutorService;
65+
import java.util.concurrent.ForkJoinPool;
6566

6667
import javax.net.ssl.HostnameVerifier;
6768
import javax.net.ssl.HttpsURLConnection;
@@ -586,4 +587,26 @@ void checkNewByteChannel(
586587
void checkName(Class<?> callerClass, FileStore that);
587588

588589
void checkType(Class<?> callerClass, FileStore that);
590+
591+
////////////////////
592+
//
593+
// Thread management
594+
//
595+
596+
void check$java_lang_Thread$start(Class<?> callerClass, Thread thread);
597+
598+
void check$java_lang_Thread$setDaemon(Class<?> callerClass, Thread thread, boolean on);
599+
600+
void check$java_lang_ThreadGroup$setDaemon(Class<?> callerClass, ThreadGroup threadGroup, boolean daemon);
601+
602+
void check$java_util_concurrent_ForkJoinPool$setParallelism(Class<?> callerClass, ForkJoinPool forkJoinPool, int size);
603+
604+
void check$java_lang_Thread$setName(Class<?> callerClass, Thread thread, String name);
605+
606+
void check$java_lang_Thread$setPriority(Class<?> callerClass, Thread thread, int newPriority);
607+
608+
void check$java_lang_Thread$setUncaughtExceptionHandler(Class<?> callerClass, Thread thread, Thread.UncaughtExceptionHandler ueh);
609+
610+
void check$java_lang_ThreadGroup$setMaxPriority(Class<?> callerClass, ThreadGroup threadGroup, int pri);
611+
589612
}

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: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -181,14 +181,18 @@ 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(VersionSpecificNioFileSystemActions.class),
191-
getTestEntries(FileStoreActions.class)
193+
getTestEntries(SpiActions.class),
194+
getTestEntries(SystemActions.class),
195+
getTestEntries(VersionSpecificNioFileSystemActions.class)
192196
)
193197
.flatMap(Function.identity())
194198
.filter(entry -> entry.getValue().fromJavaVersion() == null || Runtime.version().feature() >= entry.getValue().fromJavaVersion())
@@ -425,7 +429,9 @@ protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient cli
425429
return channel -> {
426430
logger.info("Calling check action [{}]", actionName);
427431
checkAction.action().run();
432+
logger.debug("Check action [{}] returned", actionName);
428433
channel.sendResponse(new RestResponse(RestStatus.OK, Strings.format("Succesfully executed action [%s]", actionName)));
429434
};
430435
}
436+
431437
}

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: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
import java.util.Set;
6969
import java.util.TimeZone;
7070
import java.util.concurrent.ExecutorService;
71+
import java.util.concurrent.ForkJoinPool;
7172

7273
import javax.net.ssl.HostnameVerifier;
7374
import javax.net.ssl.HttpsURLConnection;
@@ -1219,4 +1220,51 @@ public void checkName(Class<?> callerClass, FileStore that) {
12191220
public void checkType(Class<?> callerClass, FileStore that) {
12201221
policyManager.checkReadStoreAttributes(callerClass);
12211222
}
1223+
1224+
// Thread management
1225+
1226+
@Override
1227+
public void check$java_lang_Thread$start(Class<?> callerClass, Thread thread) {
1228+
policyManager.checkManageThreadsEntitlement(callerClass);
1229+
}
1230+
1231+
@Override
1232+
public void check$java_lang_Thread$setDaemon(Class<?> callerClass, Thread thread, boolean on) {
1233+
policyManager.checkManageThreadsEntitlement(callerClass);
1234+
}
1235+
1236+
@Override
1237+
public void check$java_lang_ThreadGroup$setDaemon(Class<?> callerClass, ThreadGroup threadGroup, boolean daemon) {
1238+
policyManager.checkManageThreadsEntitlement(callerClass);
1239+
}
1240+
1241+
@Override
1242+
public void check$java_util_concurrent_ForkJoinPool$setParallelism(Class<?> callerClass, ForkJoinPool forkJoinPool, int size) {
1243+
policyManager.checkManageThreadsEntitlement(callerClass);
1244+
}
1245+
1246+
@Override
1247+
public void check$java_lang_Thread$setName(Class<?> callerClass, Thread thread, String name) {
1248+
policyManager.checkManageThreadsEntitlement(callerClass);
1249+
}
1250+
1251+
@Override
1252+
public void check$java_lang_Thread$setPriority(Class<?> callerClass, Thread thread, int newPriority) {
1253+
policyManager.checkManageThreadsEntitlement(callerClass);
1254+
}
1255+
1256+
@Override
1257+
public void check$java_lang_Thread$setUncaughtExceptionHandler(
1258+
Class<?> callerClass,
1259+
Thread thread,
1260+
Thread.UncaughtExceptionHandler ueh
1261+
) {
1262+
policyManager.checkManageThreadsEntitlement(callerClass);
1263+
}
1264+
1265+
@Override
1266+
public void check$java_lang_ThreadGroup$setMaxPriority(Class<?> callerClass, ThreadGroup threadGroup, int pri) {
1267+
policyManager.checkManageThreadsEntitlement(callerClass);
1268+
}
1269+
12221270
}

0 commit comments

Comments
 (0)