Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public record BootstrapArgs(
Path configDir,
Path libDir,
Path pluginsDir,
Map<String, Path> sourcePaths,
Path logsDir,
Path tempDir,
Path pidFile,
Expand All @@ -58,6 +59,7 @@ public record BootstrapArgs(
requireNonNull(configDir);
requireNonNull(libDir);
requireNonNull(pluginsDir);
requireNonNull(sourcePaths);
requireNonNull(logsDir);
requireNonNull(tempDir);
requireNonNull(suppressFailureLogClasses);
Expand All @@ -78,10 +80,11 @@ public static BootstrapArgs bootstrapArgs() {
* @param pluginResolver a functor to map a Java Class to the plugin it belongs to (the plugin name).
* @param settingResolver a functor to resolve a setting name pattern for one or more Elasticsearch settings.
* @param dataDirs data directories for Elasticsearch
* @param sharedRepoDirs shared repository directories for Elasticsearch
* @param sharedRepoDirs shared repository directories for Elasticsearch
* @param configDir the config directory for Elasticsearch
* @param libDir the lib directory for Elasticsearch
* @param pluginsDir the directory where plugins are installed for Elasticsearch
* @param sourcePaths a map holding the path to each plugin or module jars, by plugin (or module) name.
* @param tempDir the temp directory for Elasticsearch
* @param logsDir the log directory for Elasticsearch
* @param pidFile path to a pid file for Elasticsearch, or {@code null} if one was not specified
Expand All @@ -96,6 +99,7 @@ public static void bootstrap(
Path configDir,
Path libDir,
Path pluginsDir,
Map<String, Path> sourcePaths,
Path logsDir,
Path tempDir,
Path pidFile,
Expand All @@ -114,6 +118,7 @@ public static void bootstrap(
configDir,
libDir,
pluginsDir,
sourcePaths,
logsDir,
tempDir,
pidFile,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,12 +279,12 @@ private static PolicyManager createPolicyManager() {
)
)
);
var resolver = EntitlementBootstrap.bootstrapArgs().pluginResolver();
return new PolicyManager(
serverPolicy,
agentEntitlements,
pluginPolicies,
resolver,
EntitlementBootstrap.bootstrapArgs().pluginResolver(),
EntitlementBootstrap.bootstrapArgs().sourcePaths(),
AGENTS_PACKAGE_NAME,
ENTITLEMENTS_MODULE,
pathLookup,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

package org.elasticsearch.entitlement.runtime.policy;

import org.elasticsearch.core.Nullable;
import org.elasticsearch.core.Strings;
import org.elasticsearch.entitlement.runtime.policy.entitlements.FilesEntitlement;
import org.elasticsearch.entitlement.runtime.policy.entitlements.FilesEntitlement.Mode;
Expand Down Expand Up @@ -91,6 +92,7 @@ private FileAccessTree(
String moduleName,
FilesEntitlement filesEntitlement,
PathLookup pathLookup,
Path componentPath,
List<ExclusivePath> exclusivePaths
) {
List<String> updatedExclusivePaths = new ArrayList<>();
Expand Down Expand Up @@ -139,10 +141,13 @@ private FileAccessTree(
});
}

// everything has access to the temp dir, config dir and the jdk
// everything has access to the temp dir, config dir, to their own dir (their own jar files) and the jdk
addPathAndMaybeLink.accept(pathLookup.tempDir(), READ_WRITE);
// TODO: this grants read access to the config dir for all modules until explicit read entitlements can be added
addPathAndMaybeLink.accept(pathLookup.configDir(), Mode.READ);
if (componentPath != null) {
addPathAndMaybeLink.accept(componentPath, Mode.READ);
}

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

boolean canRead(Path path) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import java.lang.module.ModuleFinder;
import java.lang.module.ModuleReference;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -91,13 +92,17 @@ public <E extends Entitlement> Stream<E> getEntitlements(Class<E> entitlementCla
}
}

private FileAccessTree getDefaultFileAccess(String componentName, Path componentPath) {
return FileAccessTree.of(componentName, UNKNOWN_COMPONENT_NAME, FilesEntitlement.EMPTY, pathLookup, componentPath, List.of());
}

// pkg private for testing
ModuleEntitlements defaultEntitlements(String componentName) {
return new ModuleEntitlements(componentName, Map.of(), defaultFileAccess);
ModuleEntitlements defaultEntitlements(String componentName, Path componentPath) {
return new ModuleEntitlements(componentName, Map.of(), getDefaultFileAccess(componentName, componentPath));
}

// pkg private for testing
ModuleEntitlements policyEntitlements(String componentName, String moduleName, List<Entitlement> entitlements) {
ModuleEntitlements policyEntitlements(String componentName, Path componentPath, String moduleName, List<Entitlement> entitlements) {
FilesEntitlement filesEntitlement = FilesEntitlement.EMPTY;
for (Entitlement entitlement : entitlements) {
if (entitlement instanceof FilesEntitlement) {
Expand All @@ -107,7 +112,7 @@ ModuleEntitlements policyEntitlements(String componentName, String moduleName, L
return new ModuleEntitlements(
componentName,
entitlements.stream().collect(groupingBy(Entitlement::getClass)),
FileAccessTree.of(componentName, moduleName, filesEntitlement, pathLookup, exclusivePaths)
FileAccessTree.of(componentName, moduleName, filesEntitlement, pathLookup, componentPath, exclusivePaths)
);
}

Expand All @@ -118,7 +123,6 @@ ModuleEntitlements policyEntitlements(String componentName, String moduleName, L
private final Map<String, Map<String, List<Entitlement>>> pluginsEntitlements;
private final Function<Class<?>, String> pluginResolver;
private final PathLookup pathLookup;
private final FileAccessTree defaultFileAccess;
private final Set<Class<?>> mutedClasses;

public static final String ALL_UNNAMED = "ALL-UNNAMED";
Expand All @@ -139,6 +143,7 @@ private static Set<Module> findSystemModules() {
).collect(Collectors.toUnmodifiableSet());
}

private final Map<String, Path> sourcePaths;
/**
* The package name containing classes from the APM agent.
*/
Expand All @@ -161,6 +166,7 @@ public PolicyManager(
List<Entitlement> apmAgentEntitlements,
Map<String, Policy> pluginPolicies,
Function<Class<?>, String> pluginResolver,
Map<String, Path> sourcePaths,
String apmAgentPackageName,
Module entitlementsModule,
PathLookup pathLookup,
Expand All @@ -172,16 +178,10 @@ public PolicyManager(
.stream()
.collect(toUnmodifiableMap(Map.Entry::getKey, e -> buildScopeEntitlementsMap(e.getValue())));
this.pluginResolver = pluginResolver;
this.sourcePaths = sourcePaths;
this.apmAgentPackageName = apmAgentPackageName;
this.entitlementsModule = entitlementsModule;
this.pathLookup = requireNonNull(pathLookup);
this.defaultFileAccess = FileAccessTree.of(
UNKNOWN_COMPONENT_NAME,
UNKNOWN_COMPONENT_NAME,
FilesEntitlement.EMPTY,
pathLookup,
List.of()
);
this.mutedClasses = suppressFailureLogClasses;

List<ExclusiveFileEntitlement> exclusiveFileEntitlements = new ArrayList<>();
Expand Down Expand Up @@ -529,44 +529,81 @@ ModuleEntitlements getEntitlements(Class<?> requestingClass) {
private ModuleEntitlements computeEntitlements(Class<?> requestingClass) {
Module requestingModule = requestingClass.getModule();
if (isServerModule(requestingModule)) {
return getModuleScopeEntitlements(serverEntitlements, requestingModule.getName(), SERVER_COMPONENT_NAME);
return getModuleScopeEntitlements(
serverEntitlements,
requestingModule.getName(),
SERVER_COMPONENT_NAME,
getComponentPathFromClass(requestingClass)
);
}

// plugins
var pluginName = pluginResolver.apply(requestingClass);
if (pluginName != null) {
var pluginEntitlements = pluginsEntitlements.get(pluginName);
if (pluginEntitlements == null) {
return defaultEntitlements(pluginName);
return defaultEntitlements(pluginName, sourcePaths.get(pluginName));
} else {
final String scopeName;
if (requestingModule.isNamed() == false) {
scopeName = ALL_UNNAMED;
} else {
scopeName = requestingModule.getName();
}
return getModuleScopeEntitlements(pluginEntitlements, scopeName, pluginName);
return getModuleScopeEntitlements(
pluginEntitlements,
getScopeName(requestingModule),
pluginName,
sourcePaths.get(pluginName)
);
}
}

if (requestingModule.isNamed() == false && requestingClass.getPackageName().startsWith(apmAgentPackageName)) {
// The APM agent is the only thing running non-modular in the system classloader
return policyEntitlements(APM_AGENT_COMPONENT_NAME, ALL_UNNAMED, apmAgentEntitlements);
return policyEntitlements(
APM_AGENT_COMPONENT_NAME,
getComponentPathFromClass(requestingClass),
ALL_UNNAMED,
apmAgentEntitlements
);
}

return defaultEntitlements(UNKNOWN_COMPONENT_NAME, null);
}

private static String getScopeName(Module requestingModule) {
if (requestingModule.isNamed() == false) {
return ALL_UNNAMED;
} else {
return requestingModule.getName();
}
}

return defaultEntitlements(UNKNOWN_COMPONENT_NAME);
// pkg private for testing
static Path getComponentPathFromClass(Class<?> requestingClass) {
var codeSource = requestingClass.getProtectionDomain().getCodeSource();
if (codeSource == null) {
return null;
}
try {
return Paths.get(codeSource.getLocation().toURI());
} catch (Exception e) {
// If we get a URISyntaxException, or any other Exception due to an invalid URI, we return null to safely skip this location
logger.info(
"Cannot get component path for [{}]: [{}] cannot be converted to a valid Path",
requestingClass.getName(),
codeSource.getLocation().toString()
);
return null;
}
}

private ModuleEntitlements getModuleScopeEntitlements(
Map<String, List<Entitlement>> scopeEntitlements,
String moduleName,
String componentName
String scopeName,
String componentName,
Path componentPath
) {
var entitlements = scopeEntitlements.get(moduleName);
var entitlements = scopeEntitlements.get(scopeName);
if (entitlements == null) {
return defaultEntitlements(componentName);
return defaultEntitlements(componentName, componentPath);
}
return policyEntitlements(componentName, moduleName, entitlements);
return policyEntitlements(componentName, componentPath, scopeName, entitlements);
}

private static boolean isServerModule(Module requestingModule) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,13 +291,13 @@ public void testFollowLinks() throws IOException {
}

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

public void testConfigDirAccess() {
var tree = FileAccessTree.of("test-component", "test-module", FilesEntitlement.EMPTY, TEST_PATH_LOOKUP, List.of());
var tree = FileAccessTree.of("test-component", "test-module", FilesEntitlement.EMPTY, TEST_PATH_LOOKUP, null, List.of());
assertThat(tree.canRead(TEST_PATH_LOOKUP.configDir()), is(true));
assertThat(tree.canWrite(TEST_PATH_LOOKUP.configDir()), is(false));
}
Expand Down Expand Up @@ -453,6 +453,7 @@ public void testWindowsAbsolutPathAccess() {
)
),
TEST_PATH_LOOKUP,
null,
List.of()
);

Expand All @@ -464,7 +465,7 @@ public void testWindowsAbsolutPathAccess() {
}

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

static FilesEntitlement entitlement(String... values) {
Expand Down
Loading