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 @@ -128,6 +128,7 @@ private static Class<?>[] findClassesToRetransform(Class<?>[] loadedClasses, Set
private static PolicyManager createPolicyManager() {
Map<String, Policy> pluginPolicies = EntitlementBootstrap.bootstrapArgs().pluginPolicies();
Path[] dataDirs = EntitlementBootstrap.bootstrapArgs().dataDirs();
Path tempDir = EntitlementBootstrap.bootstrapArgs().tempDir();

// TODO(ES-10031): Decide what goes in the elasticsearch default policy and extend it
var serverPolicy = new Policy(
Expand Down Expand Up @@ -167,7 +168,15 @@ private static PolicyManager createPolicyManager() {
// this should be removed once https://github.com/elastic/elasticsearch/issues/109335 is completed
List<Entitlement> agentEntitlements = List.of(new CreateClassLoaderEntitlement(), new ManageThreadsEntitlement());
var resolver = EntitlementBootstrap.bootstrapArgs().pluginResolver();
return new PolicyManager(serverPolicy, agentEntitlements, pluginPolicies, resolver, AGENTS_PACKAGE_NAME, ENTITLEMENTS_MODULE);
return new PolicyManager(
serverPolicy,
agentEntitlements,
pluginPolicies,
resolver,
AGENTS_PACKAGE_NAME,
ENTITLEMENTS_MODULE,
tempDir
);
}

private static Stream<InstrumentationService.InstrumentationInfo> fileSystemProviderChecks() throws ClassNotFoundException,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,12 @@
import static org.elasticsearch.core.PathUtils.getDefaultFileSystem;

public final class FileAccessTree {
public static final FileAccessTree EMPTY = new FileAccessTree(FilesEntitlement.EMPTY);
private static final String FILE_SEPARATOR = getDefaultFileSystem().getSeparator();

private final String[] readPaths;
private final String[] writePaths;

private FileAccessTree(FilesEntitlement filesEntitlement) {
private FileAccessTree(FilesEntitlement filesEntitlement, Path tempDir) {
List<String> readPaths = new ArrayList<>();
List<String> writePaths = new ArrayList<>();
for (FilesEntitlement.FileData fileData : filesEntitlement.filesData()) {
Expand All @@ -38,15 +37,19 @@ private FileAccessTree(FilesEntitlement filesEntitlement) {
readPaths.add(path);
}

// everything has access to the temp dir
readPaths.add(tempDir.toString());
writePaths.add(tempDir.toString());

readPaths.sort(String::compareTo);
writePaths.sort(String::compareTo);

this.readPaths = readPaths.toArray(new String[0]);
this.writePaths = writePaths.toArray(new String[0]);
}

public static FileAccessTree of(FilesEntitlement filesEntitlement) {
return new FileAccessTree(filesEntitlement);
public static FileAccessTree of(FilesEntitlement filesEntitlement, Path tempDir) {
return new FileAccessTree(filesEntitlement, tempDir);
}

boolean canRead(Path path) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,24 +70,6 @@ record ModuleEntitlements(
entitlementsByType = Map.copyOf(entitlementsByType);
}

public static ModuleEntitlements none(String componentName) {
return new ModuleEntitlements(componentName, Map.of(), FileAccessTree.EMPTY);
}

public static ModuleEntitlements from(String componentName, List<Entitlement> entitlements) {
FilesEntitlement filesEntitlement = FilesEntitlement.EMPTY;
for (Entitlement entitlement : entitlements) {
if (entitlement instanceof FilesEntitlement) {
filesEntitlement = (FilesEntitlement) entitlement;
}
}
return new ModuleEntitlements(
componentName,
entitlements.stream().collect(groupingBy(Entitlement::getClass)),
FileAccessTree.of(filesEntitlement)
);
}

public boolean hasEntitlement(Class<? extends Entitlement> entitlementClass) {
return entitlementsByType.containsKey(entitlementClass);
}
Expand All @@ -101,12 +83,34 @@ public <E extends Entitlement> Stream<E> getEntitlements(Class<E> entitlementCla
}
}

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

// pkg private for testing
ModuleEntitlements policyEntitlements(String componentName, List<Entitlement> entitlements) {
FilesEntitlement filesEntitlement = FilesEntitlement.EMPTY;
for (Entitlement entitlement : entitlements) {
if (entitlement instanceof FilesEntitlement) {
filesEntitlement = (FilesEntitlement) entitlement;
}
}
return new ModuleEntitlements(
componentName,
entitlements.stream().collect(groupingBy(Entitlement::getClass)),
FileAccessTree.of(filesEntitlement, tempDir)
);
}

final Map<Module, ModuleEntitlements> moduleEntitlementsMap = new ConcurrentHashMap<>();

private final Map<String, List<Entitlement>> serverEntitlements;
private final List<Entitlement> apmAgentEntitlements;
private final Map<String, Map<String, List<Entitlement>>> pluginsEntitlements;
private final Function<Class<?>, String> pluginResolver;
private final Path tempDir;
private final FileAccessTree defaultFileAccess;

public static final String ALL_UNNAMED = "ALL-UNNAMED";

Expand Down Expand Up @@ -141,7 +145,8 @@ public PolicyManager(
Map<String, Policy> pluginPolicies,
Function<Class<?>, String> pluginResolver,
String apmAgentPackageName,
Module entitlementsModule
Module entitlementsModule,
Path tempDir
) {
this.serverEntitlements = buildScopeEntitlementsMap(requireNonNull(serverPolicy));
this.apmAgentEntitlements = apmAgentEntitlements;
Expand All @@ -151,6 +156,9 @@ public PolicyManager(
this.pluginResolver = pluginResolver;
this.apmAgentPackageName = apmAgentPackageName;
this.entitlementsModule = entitlementsModule;
this.defaultFileAccess = FileAccessTree.of(FilesEntitlement.EMPTY, tempDir);

this.tempDir = tempDir;

for (var e : serverEntitlements.entrySet()) {
validateEntitlementsPerModule(SERVER_COMPONENT_NAME, e.getKey(), e.getValue());
Expand Down Expand Up @@ -425,7 +433,7 @@ private ModuleEntitlements computeEntitlements(Class<?> requestingClass) {
if (pluginName != null) {
var pluginEntitlements = pluginsEntitlements.get(pluginName);
if (pluginEntitlements == null) {
return ModuleEntitlements.none(pluginName);
return defaultEntitlements(pluginName);
} else {
final String scopeName;
if (requestingModule.isNamed() == false) {
Expand All @@ -439,10 +447,10 @@ private ModuleEntitlements computeEntitlements(Class<?> requestingClass) {

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

return ModuleEntitlements.none(UNKNOWN_COMPONENT_NAME);
return defaultEntitlements(UNKNOWN_COMPONENT_NAME);
}

private ModuleEntitlements getModuleScopeEntitlements(
Expand All @@ -452,9 +460,9 @@ private ModuleEntitlements getModuleScopeEntitlements(
) {
var entitlements = scopeEntitlements.get(moduleName);
if (entitlements == null) {
return ModuleEntitlements.none(componentName);
return defaultEntitlements(componentName);
}
return ModuleEntitlements.from(componentName, entitlements);
return policyEntitlements(componentName, entitlements);
}

private static boolean isServerModule(Module requestingModule) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ private static Path path(String s) {
}

public void testEmpty() {
var tree = FileAccessTree.of(FilesEntitlement.EMPTY);
var tree = accessTree(FilesEntitlement.EMPTY);
assertThat(tree.canRead(path("path")), is(false));
assertThat(tree.canWrite(path("path")), is(false));
}

public void testRead() {
var tree = FileAccessTree.of(entitlement("foo", "read"));
var tree = accessTree(entitlement("foo", "read"));
assertThat(tree.canRead(path("foo")), is(true));
assertThat(tree.canRead(path("foo/subdir")), is(true));
assertThat(tree.canRead(path("food")), is(false));
Expand All @@ -54,7 +54,7 @@ public void testRead() {
}

public void testWrite() {
var tree = FileAccessTree.of(entitlement("foo", "read_write"));
var tree = accessTree(entitlement("foo", "read_write"));
assertThat(tree.canWrite(path("foo")), is(true));
assertThat(tree.canWrite(path("foo/subdir")), is(true));
assertThat(tree.canWrite(path("food")), is(false));
Expand All @@ -66,7 +66,7 @@ public void testWrite() {
}

public void testTwoPaths() {
var tree = FileAccessTree.of(entitlement("foo", "read", "bar", "read"));
var tree = accessTree(entitlement("foo", "read", "bar", "read"));
assertThat(tree.canRead(path("a")), is(false));
assertThat(tree.canRead(path("bar")), is(true));
assertThat(tree.canRead(path("bar/subdir")), is(true));
Expand All @@ -77,23 +77,23 @@ public void testTwoPaths() {
}

public void testReadWriteUnderRead() {
var tree = FileAccessTree.of(entitlement("foo", "read", "foo/bar", "read_write"));
var tree = accessTree(entitlement("foo", "read", "foo/bar", "read_write"));
assertThat(tree.canRead(path("foo")), is(true));
assertThat(tree.canWrite(path("foo")), is(false));
assertThat(tree.canRead(path("foo/bar")), is(true));
assertThat(tree.canWrite(path("foo/bar")), is(true));
}

public void testNormalizePath() {
var tree = FileAccessTree.of(entitlement("foo/../bar", "read"));
var tree = accessTree(entitlement("foo/../bar", "read"));
assertThat(tree.canRead(path("foo/../bar")), is(true));
assertThat(tree.canRead(path("foo")), is(false));
assertThat(tree.canRead(path("")), is(false));
}

public void testForwardSlashes() {
String sep = getDefaultFileSystem().getSeparator();
var tree = FileAccessTree.of(entitlement("a/b", "read", "m" + sep + "n", "read"));
var tree = accessTree(entitlement("a/b", "read", "m" + sep + "n", "read"));

// Native separators work
assertThat(tree.canRead(path("a" + sep + "b")), is(true));
Expand All @@ -104,6 +104,18 @@ public void testForwardSlashes() {
assertThat(tree.canRead(path("m/n")), is(true));
}

public void testTempDirAccess() {
Path tempDir = createTempDir();
var tree = FileAccessTree.of(FilesEntitlement.EMPTY, tempDir);

assertThat(tree.canRead(tempDir), is(true));
assertThat(tree.canWrite(tempDir), is(true));
}

FileAccessTree accessTree(FilesEntitlement entitlement) {
return FileAccessTree.of(entitlement, createTempDir());
}

FilesEntitlement entitlement(String... values) {
List<Object> filesData = new ArrayList<>();
for (int i = 0; i < values.length; i += 2) {
Expand Down
Loading