Skip to content

Commit aa182af

Browse files
Add ThreadContextPermission for markAsSystemContext and allow core to perform the method (#15016)
* Add RuntimePermission for markAsSystemContext and allow core to perform the method Signed-off-by: Craig Perkins <[email protected]> * private Signed-off-by: Craig Perkins <[email protected]> * Surround with doPrivileged Signed-off-by: Craig Perkins <[email protected]> * Create ThreadContextAccess Signed-off-by: Craig Perkins <[email protected]> * Create notion of ThreadContextPermission Signed-off-by: Craig Perkins <[email protected]> * Add to CHANGELOG Signed-off-by: Craig Perkins <[email protected]> * Add javadoc Signed-off-by: Craig Perkins <[email protected]> * Add to test-framework.policy file Signed-off-by: Craig Perkins <[email protected]> * Mark as internal Signed-off-by: Craig Perkins <[email protected]> --------- Signed-off-by: Craig Perkins <[email protected]> (cherry picked from commit 597747d) Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 154b284 commit aa182af

File tree

19 files changed

+128
-16
lines changed

19 files changed

+128
-16
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
88
- Fix for hasInitiatedFetching to fix allocation explain and manual reroute APIs (([#14972](https://github.com/opensearch-project/OpenSearch/pull/14972))
99
- Add setting to ignore throttling nodes for allocation of unassigned primaries in remote restore ([#14991](https://github.com/opensearch-project/OpenSearch/pull/14991))
1010
- Add basic aggregation support for derived fields ([#14618](https://github.com/opensearch-project/OpenSearch/pull/14618))
11+
- Add ThreadContextPermission for markAsSystemContext and allow core to perform the method ([#15016](https://github.com/opensearch-project/OpenSearch/pull/15016))
1112

1213
### Dependencies
1314
- Bump `org.apache.commons:commons-lang3` from 3.14.0 to 3.15.0 ([#14861](https://github.com/opensearch-project/OpenSearch/pull/14861))
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*/
8+
9+
package org.opensearch.secure_sm;
10+
11+
import java.security.BasicPermission;
12+
13+
/**
14+
* Permission to utilize methods in the ThreadContext class that are normally not accessible
15+
*
16+
* @see ThreadGroup
17+
* @see SecureSM
18+
*/
19+
public final class ThreadContextPermission extends BasicPermission {
20+
21+
/**
22+
* Creates a new ThreadContextPermission object.
23+
*
24+
* @param name target name
25+
*/
26+
public ThreadContextPermission(String name) {
27+
super(name);
28+
}
29+
30+
/**
31+
* Creates a new ThreadContextPermission object.
32+
* This constructor exists for use by the {@code Policy} object to instantiate new Permission objects.
33+
*
34+
* @param name target name
35+
* @param actions ignored
36+
*/
37+
public ThreadContextPermission(String name, String actions) {
38+
super(name, actions);
39+
}
40+
}

server/src/main/java/org/opensearch/cluster/service/ClusterApplierService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
import org.opensearch.common.util.concurrent.OpenSearchExecutors;
6262
import org.opensearch.common.util.concurrent.PrioritizedOpenSearchThreadPoolExecutor;
6363
import org.opensearch.common.util.concurrent.ThreadContext;
64+
import org.opensearch.common.util.concurrent.ThreadContextAccess;
6465
import org.opensearch.core.concurrency.OpenSearchRejectedExecutionException;
6566
import org.opensearch.telemetry.metrics.noop.NoopMetricsRegistry;
6667
import org.opensearch.telemetry.metrics.tags.Tags;
@@ -396,7 +397,7 @@ private void submitStateUpdateTask(
396397
final ThreadContext threadContext = threadPool.getThreadContext();
397398
final Supplier<ThreadContext.StoredContext> supplier = threadContext.newRestorableContext(true);
398399
try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
399-
threadContext.markAsSystemContext();
400+
ThreadContextAccess.doPrivilegedVoid(threadContext::markAsSystemContext);
400401
final UpdateTask updateTask = new UpdateTask(
401402
config.priority(),
402403
source,

server/src/main/java/org/opensearch/cluster/service/MasterService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
import org.opensearch.common.util.concurrent.OpenSearchExecutors;
6767
import org.opensearch.common.util.concurrent.PrioritizedOpenSearchThreadPoolExecutor;
6868
import org.opensearch.common.util.concurrent.ThreadContext;
69+
import org.opensearch.common.util.concurrent.ThreadContextAccess;
6970
import org.opensearch.core.Assertions;
7071
import org.opensearch.core.common.text.Text;
7172
import org.opensearch.core.concurrency.OpenSearchRejectedExecutionException;
@@ -1022,7 +1023,7 @@ public <T> void submitStateUpdateTasks(
10221023
final ThreadContext threadContext = threadPool.getThreadContext();
10231024
final Supplier<ThreadContext.StoredContext> supplier = threadContext.newRestorableContext(true);
10241025
try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
1025-
threadContext.markAsSystemContext();
1026+
ThreadContextAccess.doPrivilegedVoid(threadContext::markAsSystemContext);
10261027

10271028
List<Batcher.UpdateTask> safeTasks = tasks.entrySet()
10281029
.stream()

server/src/main/java/org/opensearch/common/util/concurrent/ThreadContext.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,13 @@
4545
import org.opensearch.core.common.io.stream.StreamOutput;
4646
import org.opensearch.core.common.io.stream.Writeable;
4747
import org.opensearch.http.HttpTransportSettings;
48+
import org.opensearch.secure_sm.ThreadContextPermission;
4849
import org.opensearch.tasks.Task;
4950
import org.opensearch.tasks.TaskThreadContextStatePropagator;
5051

5152
import java.io.IOException;
5253
import java.nio.charset.StandardCharsets;
54+
import java.security.Permission;
5355
import java.util.ArrayList;
5456
import java.util.Collection;
5557
import java.util.Collections;
@@ -111,6 +113,10 @@ public final class ThreadContext implements Writeable {
111113
*/
112114
public static final String ACTION_ORIGIN_TRANSIENT_NAME = "action.origin";
113115

116+
// thread context permissions
117+
118+
private static final Permission ACCESS_SYSTEM_THREAD_CONTEXT_PERMISSION = new ThreadContextPermission("markAsSystemContext");
119+
114120
private static final Logger logger = LogManager.getLogger(ThreadContext.class);
115121
private static final ThreadContextStruct DEFAULT_CONTEXT = new ThreadContextStruct();
116122
private final Map<String, String> defaultHeader;
@@ -554,8 +560,19 @@ boolean isDefaultContext() {
554560
/**
555561
* Marks this thread context as an internal system context. This signals that actions in this context are issued
556562
* by the system itself rather than by a user action.
563+
*
564+
* Usage of markAsSystemContext is guarded by a ThreadContextPermission. In order to use
565+
* markAsSystemContext, the codebase needs to explicitly be granted permission in the JSM policy file.
566+
*
567+
* Add an entry in the grant portion of the policy file like this:
568+
*
569+
* permission org.opensearch.secure_sm.ThreadContextPermission "markAsSystemContext";
557570
*/
558571
public void markAsSystemContext() {
572+
SecurityManager sm = System.getSecurityManager();
573+
if (sm != null) {
574+
sm.checkPermission(ACCESS_SYSTEM_THREAD_CONTEXT_PERMISSION);
575+
}
559576
threadLocal.set(threadLocal.get().setSystemContext(propagators));
560577
}
561578

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*/
8+
9+
package org.opensearch.common.util.concurrent;
10+
11+
import org.opensearch.SpecialPermission;
12+
import org.opensearch.common.annotation.InternalApi;
13+
14+
import java.security.AccessController;
15+
import java.security.PrivilegedAction;
16+
17+
/**
18+
* This class wraps the {@link ThreadContext} operations requiring access in
19+
* {@link AccessController#doPrivileged(PrivilegedAction)} blocks.
20+
*
21+
* @opensearch.internal
22+
*/
23+
@SuppressWarnings("removal")
24+
@InternalApi
25+
public final class ThreadContextAccess {
26+
27+
private ThreadContextAccess() {}
28+
29+
public static <T> T doPrivileged(PrivilegedAction<T> operation) {
30+
SpecialPermission.check();
31+
return AccessController.doPrivileged(operation);
32+
}
33+
34+
public static void doPrivilegedVoid(Runnable action) {
35+
SpecialPermission.check();
36+
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
37+
action.run();
38+
return null;
39+
});
40+
}
41+
}

server/src/main/java/org/opensearch/index/seqno/GlobalCheckpointSyncAction.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import org.opensearch.common.inject.Inject;
4545
import org.opensearch.common.settings.Settings;
4646
import org.opensearch.common.util.concurrent.ThreadContext;
47+
import org.opensearch.common.util.concurrent.ThreadContextAccess;
4748
import org.opensearch.core.action.ActionListener;
4849
import org.opensearch.core.common.io.stream.StreamInput;
4950
import org.opensearch.core.index.shard.ShardId;
@@ -98,7 +99,7 @@ public GlobalCheckpointSyncAction(
9899
public void updateGlobalCheckpointForShard(final ShardId shardId) {
99100
final ThreadContext threadContext = threadPool.getThreadContext();
100101
try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
101-
threadContext.markAsSystemContext();
102+
ThreadContextAccess.doPrivilegedVoid(threadContext::markAsSystemContext);
102103
execute(new Request(shardId), ActionListener.wrap(r -> {}, e -> {
103104
if (ExceptionsHelper.unwrap(e, AlreadyClosedException.class, IndexShardClosedException.class) == null) {
104105
logger.info(new ParameterizedMessage("{} global checkpoint sync failed", shardId), e);

server/src/main/java/org/opensearch/index/seqno/RetentionLeaseBackgroundSyncAction.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import org.opensearch.common.inject.Inject;
4949
import org.opensearch.common.settings.Settings;
5050
import org.opensearch.common.util.concurrent.ThreadContext;
51+
import org.opensearch.common.util.concurrent.ThreadContextAccess;
5152
import org.opensearch.core.action.ActionListener;
5253
import org.opensearch.core.common.io.stream.StreamInput;
5354
import org.opensearch.core.common.io.stream.StreamOutput;
@@ -122,7 +123,7 @@ final void backgroundSync(ShardId shardId, String primaryAllocationId, long prim
122123
final ThreadContext threadContext = threadPool.getThreadContext();
123124
try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
124125
// we have to execute under the system context so that if security is enabled the sync is authorized
125-
threadContext.markAsSystemContext();
126+
ThreadContextAccess.doPrivilegedVoid(threadContext::markAsSystemContext);
126127
final Request request = new Request(shardId, retentionLeases);
127128
final ReplicationTask task = (ReplicationTask) taskManager.register("transport", "retention_lease_background_sync", request);
128129
transportService.sendChildRequest(

server/src/main/java/org/opensearch/index/seqno/RetentionLeaseSyncAction.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import org.opensearch.common.inject.Inject;
5151
import org.opensearch.common.settings.Settings;
5252
import org.opensearch.common.util.concurrent.ThreadContext;
53+
import org.opensearch.common.util.concurrent.ThreadContextAccess;
5354
import org.opensearch.core.action.ActionListener;
5455
import org.opensearch.core.common.io.stream.StreamInput;
5556
import org.opensearch.core.common.io.stream.StreamOutput;
@@ -137,7 +138,7 @@ final void sync(
137138
final ThreadContext threadContext = threadPool.getThreadContext();
138139
try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
139140
// we have to execute under the system context so that if security is enabled the sync is authorized
140-
threadContext.markAsSystemContext();
141+
ThreadContextAccess.doPrivilegedVoid(threadContext::markAsSystemContext);
141142
final Request request = new Request(shardId, retentionLeases);
142143
final ReplicationTask task = (ReplicationTask) taskManager.register("transport", "retention_lease_sync", request);
143144
transportService.sendChildRequest(

server/src/main/java/org/opensearch/indices/replication/checkpoint/PublishCheckpointAction.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.opensearch.common.inject.Inject;
2525
import org.opensearch.common.settings.Settings;
2626
import org.opensearch.common.util.concurrent.ThreadContext;
27+
import org.opensearch.common.util.concurrent.ThreadContextAccess;
2728
import org.opensearch.core.action.ActionListener;
2829
import org.opensearch.core.common.io.stream.StreamInput;
2930
import org.opensearch.index.IndexNotFoundException;
@@ -113,7 +114,7 @@ final void publish(IndexShard indexShard, ReplicationCheckpoint checkpoint) {
113114
final ThreadContext threadContext = threadPool.getThreadContext();
114115
try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
115116
// we have to execute under the system context so that if security is enabled the sync is authorized
116-
threadContext.markAsSystemContext();
117+
ThreadContextAccess.doPrivilegedVoid(threadContext::markAsSystemContext);
117118
PublishCheckpointRequest request = new PublishCheckpointRequest(checkpoint);
118119
final ReplicationTask task = (ReplicationTask) taskManager.register("transport", "segrep_publish_checkpoint", request);
119120
final ReplicationTimer timer = new ReplicationTimer();

0 commit comments

Comments
 (0)