Skip to content

Commit 3cafc5d

Browse files
committed
allow read access for each plugin to its own directory
1 parent 106d7ea commit 3cafc5d

File tree

7 files changed

+57
-18
lines changed

7 files changed

+57
-18
lines changed

libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/EntitlementBootstrap.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public record BootstrapArgs(
4141
Path configDir,
4242
Path libDir,
4343
Path pluginsDir,
44+
Map<String, Path> bundlesDirs,
4445
Path logsDir,
4546
Path tempDir,
4647
Path pidFile,
@@ -58,6 +59,7 @@ public record BootstrapArgs(
5859
requireNonNull(configDir);
5960
requireNonNull(libDir);
6061
requireNonNull(pluginsDir);
62+
requireNonNull(bundlesDirs);
6163
requireNonNull(logsDir);
6264
requireNonNull(tempDir);
6365
requireNonNull(suppressFailureLogClasses);
@@ -78,10 +80,11 @@ public static BootstrapArgs bootstrapArgs() {
7880
* @param pluginResolver a functor to map a Java Class to the plugin it belongs to (the plugin name).
7981
* @param settingResolver a functor to resolve a setting name pattern for one or more Elasticsearch settings.
8082
* @param dataDirs data directories for Elasticsearch
81-
* @param sharedRepoDirs shared repository directories for Elasticsearch
83+
* @param sharedRepoDirs shared repository directories for Elasticsearch
8284
* @param configDir the config directory for Elasticsearch
8385
* @param libDir the lib directory for Elasticsearch
8486
* @param pluginsDir the directory where plugins are installed for Elasticsearch
87+
* @param bundlesDirs a map holding the path for each plugin or module, by plugin (or module) name.
8588
* @param tempDir the temp directory for Elasticsearch
8689
* @param logsDir the log directory for Elasticsearch
8790
* @param pidFile path to a pid file for Elasticsearch, or {@code null} if one was not specified
@@ -96,6 +99,7 @@ public static void bootstrap(
9699
Path configDir,
97100
Path libDir,
98101
Path pluginsDir,
102+
Map<String, Path> bundlesDirs,
99103
Path logsDir,
100104
Path tempDir,
101105
Path pidFile,
@@ -114,6 +118,7 @@ public static void bootstrap(
114118
configDir,
115119
libDir,
116120
pluginsDir,
121+
bundlesDirs,
117122
logsDir,
118123
tempDir,
119124
pidFile,

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,12 +279,12 @@ private static PolicyManager createPolicyManager() {
279279
)
280280
)
281281
);
282-
var resolver = EntitlementBootstrap.bootstrapArgs().pluginResolver();
283282
return new PolicyManager(
284283
serverPolicy,
285284
agentEntitlements,
286285
pluginPolicies,
287-
resolver,
286+
EntitlementBootstrap.bootstrapArgs().pluginResolver(),
287+
EntitlementBootstrap.bootstrapArgs().bundlesDirs(),
288288
AGENTS_PACKAGE_NAME,
289289
ENTITLEMENTS_MODULE,
290290
pathLookup,

libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/FileAccessTree.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
package org.elasticsearch.entitlement.runtime.policy;
1111

12+
import org.elasticsearch.core.Nullable;
1213
import org.elasticsearch.core.Strings;
1314
import org.elasticsearch.entitlement.runtime.policy.entitlements.FilesEntitlement;
1415
import org.elasticsearch.entitlement.runtime.policy.entitlements.FilesEntitlement.Mode;
@@ -91,6 +92,7 @@ private FileAccessTree(
9192
String moduleName,
9293
FilesEntitlement filesEntitlement,
9394
PathLookup pathLookup,
95+
Path componentDir,
9496
List<ExclusivePath> exclusivePaths
9597
) {
9698
List<String> updatedExclusivePaths = new ArrayList<>();
@@ -139,10 +141,13 @@ private FileAccessTree(
139141
});
140142
}
141143

142-
// everything has access to the temp dir, config dir and the jdk
144+
// everything has access to the temp dir, config dir, to their own dir (their own jar files) and the jdk
143145
addPathAndMaybeLink.accept(pathLookup.tempDir(), READ_WRITE);
144146
// TODO: this grants read access to the config dir for all modules until explicit read entitlements can be added
145147
addPathAndMaybeLink.accept(pathLookup.configDir(), Mode.READ);
148+
if (componentDir != null) {
149+
addPathAndMaybeLink.accept(componentDir, Mode.READ);
150+
}
146151

147152
// TODO: watcher uses javax.activation which looks for known mime types configuration, should this be global or explicit in watcher?
148153
Path jdk = Paths.get(System.getProperty("java.home"));
@@ -179,9 +184,10 @@ public static FileAccessTree of(
179184
String moduleName,
180185
FilesEntitlement filesEntitlement,
181186
PathLookup pathLookup,
187+
@Nullable Path componentDir,
182188
List<ExclusivePath> exclusivePaths
183189
) {
184-
return new FileAccessTree(componentName, moduleName, filesEntitlement, pathLookup, exclusivePaths);
190+
return new FileAccessTree(componentName, moduleName, filesEntitlement, pathLookup, componentDir, exclusivePaths);
185191
}
186192

187193
boolean canRead(Path path) {

libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,20 @@ public <E extends Entitlement> Stream<E> getEntitlements(Class<E> entitlementCla
9191
}
9292
}
9393

94+
private FileAccessTree getDefaultFileAccess(String componentName) {
95+
return FileAccessTree.of(
96+
componentName,
97+
UNKNOWN_COMPONENT_NAME,
98+
FilesEntitlement.EMPTY,
99+
pathLookup,
100+
bundlesDirs.get(componentName),
101+
List.of()
102+
);
103+
}
104+
94105
// pkg private for testing
95106
ModuleEntitlements defaultEntitlements(String componentName) {
96-
return new ModuleEntitlements(componentName, Map.of(), defaultFileAccess);
107+
return new ModuleEntitlements(componentName, Map.of(), getDefaultFileAccess(componentName));
97108
}
98109

99110
// pkg private for testing
@@ -107,7 +118,7 @@ ModuleEntitlements policyEntitlements(String componentName, String moduleName, L
107118
return new ModuleEntitlements(
108119
componentName,
109120
entitlements.stream().collect(groupingBy(Entitlement::getClass)),
110-
FileAccessTree.of(componentName, moduleName, filesEntitlement, pathLookup, exclusivePaths)
121+
FileAccessTree.of(componentName, moduleName, filesEntitlement, pathLookup, bundlesDirs.get(componentName), exclusivePaths)
111122
);
112123
}
113124

@@ -118,7 +129,6 @@ ModuleEntitlements policyEntitlements(String componentName, String moduleName, L
118129
private final Map<String, Map<String, List<Entitlement>>> pluginsEntitlements;
119130
private final Function<Class<?>, String> pluginResolver;
120131
private final PathLookup pathLookup;
121-
private final FileAccessTree defaultFileAccess;
122132
private final Set<Class<?>> mutedClasses;
123133

124134
public static final String ALL_UNNAMED = "ALL-UNNAMED";
@@ -139,6 +149,7 @@ private static Set<Module> findSystemModules() {
139149
).collect(Collectors.toUnmodifiableSet());
140150
}
141151

152+
private final Map<String, Path> bundlesDirs;
142153
/**
143154
* The package name containing classes from the APM agent.
144155
*/
@@ -161,6 +172,7 @@ public PolicyManager(
161172
List<Entitlement> apmAgentEntitlements,
162173
Map<String, Policy> pluginPolicies,
163174
Function<Class<?>, String> pluginResolver,
175+
Map<String, Path> bundlesDirs,
164176
String apmAgentPackageName,
165177
Module entitlementsModule,
166178
PathLookup pathLookup,
@@ -172,16 +184,10 @@ public PolicyManager(
172184
.stream()
173185
.collect(toUnmodifiableMap(Map.Entry::getKey, e -> buildScopeEntitlementsMap(e.getValue())));
174186
this.pluginResolver = pluginResolver;
187+
this.bundlesDirs = bundlesDirs;
175188
this.apmAgentPackageName = apmAgentPackageName;
176189
this.entitlementsModule = entitlementsModule;
177190
this.pathLookup = requireNonNull(pathLookup);
178-
this.defaultFileAccess = FileAccessTree.of(
179-
UNKNOWN_COMPONENT_NAME,
180-
UNKNOWN_COMPONENT_NAME,
181-
FilesEntitlement.EMPTY,
182-
pathLookup,
183-
List.of()
184-
);
185191
this.mutedClasses = suppressFailureLogClasses;
186192

187193
List<ExclusiveFileEntitlement> exclusiveFileEntitlements = new ArrayList<>();

libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/FileAccessTreeTests.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -291,13 +291,13 @@ public void testFollowLinks() throws IOException {
291291
}
292292

293293
public void testTempDirAccess() {
294-
var tree = FileAccessTree.of("test-component", "test-module", FilesEntitlement.EMPTY, TEST_PATH_LOOKUP, List.of());
294+
var tree = FileAccessTree.of("test-component", "test-module", FilesEntitlement.EMPTY, TEST_PATH_LOOKUP, null, List.of());
295295
assertThat(tree.canRead(TEST_PATH_LOOKUP.tempDir()), is(true));
296296
assertThat(tree.canWrite(TEST_PATH_LOOKUP.tempDir()), is(true));
297297
}
298298

299299
public void testConfigDirAccess() {
300-
var tree = FileAccessTree.of("test-component", "test-module", FilesEntitlement.EMPTY, TEST_PATH_LOOKUP, List.of());
300+
var tree = FileAccessTree.of("test-component", "test-module", FilesEntitlement.EMPTY, TEST_PATH_LOOKUP, null, List.of());
301301
assertThat(tree.canRead(TEST_PATH_LOOKUP.configDir()), is(true));
302302
assertThat(tree.canWrite(TEST_PATH_LOOKUP.configDir()), is(false));
303303
}
@@ -453,6 +453,7 @@ public void testWindowsAbsolutPathAccess() {
453453
)
454454
),
455455
TEST_PATH_LOOKUP,
456+
null,
456457
List.of()
457458
);
458459

@@ -464,7 +465,7 @@ public void testWindowsAbsolutPathAccess() {
464465
}
465466

466467
FileAccessTree accessTree(FilesEntitlement entitlement, List<ExclusivePath> exclusivePaths) {
467-
return FileAccessTree.of("test-component", "test-module", entitlement, TEST_PATH_LOOKUP, exclusivePaths);
468+
return FileAccessTree.of("test-component", "test-module", entitlement, TEST_PATH_LOOKUP, null, exclusivePaths);
468469
}
469470

470471
static FilesEntitlement entitlement(String... values) {

libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/PolicyManagerTests.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ public void testGetEntitlementsThrowsOnMissingPluginUnnamedModule() {
8585
List.of(),
8686
Map.of("plugin1", createPluginPolicy("plugin.module")),
8787
c -> "plugin1",
88+
Map.of("plugin1", Path.of("modules", "plugin1")),
8889
TEST_AGENTS_PACKAGE_NAME,
8990
NO_ENTITLEMENTS_MODULE,
9091
TEST_PATH_LOOKUP,
@@ -110,6 +111,7 @@ public void testGetEntitlementsThrowsOnMissingPolicyForPlugin() {
110111
List.of(),
111112
Map.of(),
112113
c -> "plugin1",
114+
Map.of("plugin1", Path.of("modules", "plugin1")),
113115
TEST_AGENTS_PACKAGE_NAME,
114116
NO_ENTITLEMENTS_MODULE,
115117
TEST_PATH_LOOKUP,
@@ -131,6 +133,7 @@ public void testGetEntitlementsFailureIsCached() {
131133
List.of(),
132134
Map.of(),
133135
c -> "plugin1",
136+
Map.of("plugin1", Path.of("modules", "plugin1")),
134137
TEST_AGENTS_PACKAGE_NAME,
135138
NO_ENTITLEMENTS_MODULE,
136139
TEST_PATH_LOOKUP,
@@ -157,6 +160,7 @@ public void testGetEntitlementsReturnsEntitlementsForPluginUnnamedModule() {
157160
List.of(),
158161
Map.ofEntries(entry("plugin2", createPluginPolicy(ALL_UNNAMED))),
159162
c -> "plugin2",
163+
Map.of("plugin2", Path.of("modules", "plugin2")),
160164
TEST_AGENTS_PACKAGE_NAME,
161165
NO_ENTITLEMENTS_MODULE,
162166
TEST_PATH_LOOKUP,
@@ -176,6 +180,7 @@ public void testGetEntitlementsThrowsOnMissingPolicyForServer() throws ClassNotF
176180
List.of(),
177181
Map.of(),
178182
c -> null,
183+
Map.of(),
179184
TEST_AGENTS_PACKAGE_NAME,
180185
NO_ENTITLEMENTS_MODULE,
181186
TEST_PATH_LOOKUP,
@@ -207,6 +212,7 @@ public void testGetEntitlementsReturnsEntitlementsForServerModule() throws Class
207212
List.of(),
208213
Map.of(),
209214
c -> null,
215+
Map.of(),
210216
TEST_AGENTS_PACKAGE_NAME,
211217
NO_ENTITLEMENTS_MODULE,
212218
TEST_PATH_LOOKUP,
@@ -234,6 +240,7 @@ public void testGetEntitlementsReturnsEntitlementsForPluginModule() throws IOExc
234240
List.of(),
235241
Map.of("mock-plugin", createPluginPolicy("org.example.plugin")),
236242
c -> "mock-plugin",
243+
Map.of("mock-plugin", Path.of("modules", "mock-plugin")),
237244
TEST_AGENTS_PACKAGE_NAME,
238245
NO_ENTITLEMENTS_MODULE,
239246
TEST_PATH_LOOKUP,
@@ -254,6 +261,7 @@ public void testGetEntitlementsResultIsCached() {
254261
List.of(),
255262
Map.ofEntries(entry("plugin2", createPluginPolicy(ALL_UNNAMED))),
256263
c -> "plugin2",
264+
Map.of("plugin2", Path.of("modules", "plugin2")),
257265
TEST_AGENTS_PACKAGE_NAME,
258266
NO_ENTITLEMENTS_MODULE,
259267
TEST_PATH_LOOKUP,
@@ -314,6 +322,7 @@ public void testAgentsEntitlements() throws IOException, ClassNotFoundException
314322
List.of(new CreateClassLoaderEntitlement()),
315323
Map.of(),
316324
c -> c.getPackageName().startsWith(TEST_AGENTS_PACKAGE_NAME) ? null : "test",
325+
Map.of(),
317326
TEST_AGENTS_PACKAGE_NAME,
318327
NO_ENTITLEMENTS_MODULE,
319328
TEST_PATH_LOOKUP,
@@ -343,6 +352,7 @@ public void testDuplicateEntitlements() {
343352
List.of(),
344353
Map.of(),
345354
c -> "test",
355+
Map.of(),
346356
TEST_AGENTS_PACKAGE_NAME,
347357
NO_ENTITLEMENTS_MODULE,
348358
TEST_PATH_LOOKUP,
@@ -361,6 +371,7 @@ public void testDuplicateEntitlements() {
361371
List.of(new CreateClassLoaderEntitlement(), new CreateClassLoaderEntitlement()),
362372
Map.of(),
363373
c -> "test",
374+
Map.of(),
364375
TEST_AGENTS_PACKAGE_NAME,
365376
NO_ENTITLEMENTS_MODULE,
366377
TEST_PATH_LOOKUP,
@@ -399,6 +410,7 @@ public void testDuplicateEntitlements() {
399410
)
400411
),
401412
c -> "plugin1",
413+
Map.of("plugin1", Path.of("modules", "plugin1")),
402414
TEST_AGENTS_PACKAGE_NAME,
403415
NO_ENTITLEMENTS_MODULE,
404416
TEST_PATH_LOOKUP,
@@ -451,6 +463,7 @@ public void testFilesEntitlementsWithExclusive() {
451463
)
452464
),
453465
c -> "",
466+
Map.of("plugin1", Path.of("modules", "plugin1"), "plugin2", Path.of("modules", "plugin2")),
454467
TEST_AGENTS_PACKAGE_NAME,
455468
NO_ENTITLEMENTS_MODULE,
456469
TEST_PATH_LOOKUP,
@@ -497,6 +510,7 @@ public void testFilesEntitlementsWithExclusive() {
497510
)
498511
),
499512
c -> "",
513+
Map.of(),
500514
TEST_AGENTS_PACKAGE_NAME,
501515
NO_ENTITLEMENTS_MODULE,
502516
TEST_PATH_LOOKUP,
@@ -523,6 +537,7 @@ public void testPluginResolverOverridesAgents() {
523537
List.of(new CreateClassLoaderEntitlement()),
524538
Map.of(),
525539
c -> "test", // Insist that the class is in a plugin
540+
Map.of(),
526541
TEST_AGENTS_PACKAGE_NAME,
527542
NO_ENTITLEMENTS_MODULE,
528543
TEST_PATH_LOOKUP,
@@ -545,6 +560,7 @@ private static PolicyManager policyManager(String agentsPackageName, Module enti
545560
List.of(),
546561
Map.of(),
547562
c -> "test",
563+
Map.of(),
548564
agentsPackageName,
549565
entitlementsModule,
550566
TEST_PATH_LOOKUP,

server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import org.elasticsearch.nativeaccess.NativeAccess;
4949
import org.elasticsearch.node.Node;
5050
import org.elasticsearch.node.NodeValidationException;
51+
import org.elasticsearch.plugins.PluginBundle;
5152
import org.elasticsearch.plugins.PluginsLoader;
5253
import org.elasticsearch.rest.MethodHandlers;
5354
import org.elasticsearch.transport.RequestHandlerRegistry;
@@ -70,6 +71,7 @@
7071
import java.util.Set;
7172
import java.util.concurrent.CountDownLatch;
7273
import java.util.concurrent.TimeUnit;
74+
import java.util.stream.Collectors;
7375
import java.util.stream.Stream;
7476

7577
import static org.elasticsearch.bootstrap.BootstrapSettings.SECURITY_FILTER_BAD_DEFAULTS_SETTING;
@@ -245,6 +247,8 @@ private static void initPhase2(Bootstrap bootstrap) throws IOException {
245247
pluginsLoader = PluginsLoader.createPluginsLoader(modulesBundles, pluginsBundles, findPluginsWithNativeAccess(pluginPolicies));
246248

247249
var pluginsResolver = PluginsResolver.create(pluginsLoader);
250+
Map<String, Path> bundlesDirs = Stream.concat(modulesBundles.stream(), pluginsBundles.stream())
251+
.collect(Collectors.toUnmodifiableMap(bundle -> bundle.pluginDescriptor().getName(), PluginBundle::getDir));
248252
EntitlementBootstrap.bootstrap(
249253
pluginPolicies,
250254
pluginsResolver::resolveClassToPluginName,
@@ -254,6 +258,7 @@ private static void initPhase2(Bootstrap bootstrap) throws IOException {
254258
nodeEnv.configDir(),
255259
nodeEnv.libDir(),
256260
nodeEnv.pluginsDir(),
261+
bundlesDirs,
257262
nodeEnv.logsDir(),
258263
nodeEnv.tmpDir(),
259264
args.pidFile(),

0 commit comments

Comments
 (0)