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 @@ -161,7 +161,7 @@ private static PolicyManager createPolicyManager(
PathLookup pathLookup,
Policy serverPolicyPatch,
Function<Class<?>, PolicyManager.PolicyScope> scopeResolver,
Map<String, Collection<Path>> pluginSourcePaths
Map<String, Collection<Path>> pluginSourcePathsResolver
) {
FilesEntitlementsValidation.validate(pluginPolicies, pathLookup);

Expand All @@ -170,7 +170,7 @@ private static PolicyManager createPolicyManager(
HardcodedEntitlements.agentEntitlements(),
pluginPolicies,
scopeResolver,
pluginSourcePaths,
pluginSourcePathsResolver::get,
pathLookup
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@

package org.elasticsearch.entitlement.runtime.policy;

import org.elasticsearch.core.PathUtils;

import java.nio.file.Path;
import java.util.stream.Stream;

/**
* Resolves paths for known directories checked by entitlements.
*/
public interface PathLookup {
Class<?> DEFAULT_FILESYSTEM_CLASS = PathUtils.getDefaultFileSystem().getClass();

enum BaseDir {
USER_HOME,
CONFIG,
Expand All @@ -37,4 +41,6 @@ enum BaseDir {
* paths of the given {@code baseDir}.
*/
Stream<Path> resolveSettingPaths(BaseDir baseDir, String settingName);

boolean isPathOnDefaultFilesystem(Path path);
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,9 @@ public Stream<Path> resolveSettingPaths(BaseDir baseDir, String settingName) {
.toList();
return getBaseDirPaths(baseDir).flatMap(path -> relativePaths.stream().map(path::resolve));
}

@Override
public boolean isPathOnDefaultFilesystem(Path path) {
return path.getFileSystem().getClass() == DEFAULT_FILESYSTEM_CLASS;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

package org.elasticsearch.entitlement.runtime.policy;

import org.elasticsearch.core.PathUtils;
import org.elasticsearch.core.Strings;
import org.elasticsearch.core.SuppressForbidden;
import org.elasticsearch.entitlement.instrumentation.InstrumentationService;
Expand Down Expand Up @@ -58,7 +57,7 @@
*/
@SuppressForbidden(reason = "Explicitly checking APIs that are forbidden")
public class PolicyCheckerImpl implements PolicyChecker {
static final Class<?> DEFAULT_FILESYSTEM_CLASS = PathUtils.getDefaultFileSystem().getClass();

protected final Set<Package> suppressFailureLogPackages;
/**
* Frames originating from this module are ignored in the permission logic.
Expand All @@ -81,15 +80,14 @@ public PolicyCheckerImpl(
this.pathLookup = pathLookup;
}

private static boolean isPathOnDefaultFilesystem(Path path) {
var pathFileSystemClass = path.getFileSystem().getClass();
if (path.getFileSystem().getClass() != DEFAULT_FILESYSTEM_CLASS) {
private boolean isPathOnDefaultFilesystem(Path path) {
if (pathLookup.isPathOnDefaultFilesystem(path) == false) {
PolicyManager.generalLogger.trace(
() -> Strings.format(
"File entitlement trivially allowed: path [%s] is for a different FileSystem class [%s], default is [%s]",
path.toString(),
pathFileSystemClass.getName(),
DEFAULT_FILESYSTEM_CLASS.getName()
path.getFileSystem().getClass().getName(),
PathLookup.DEFAULT_FILESYSTEM_CLASS.getName()
)
);
return false;
Expand Down Expand Up @@ -217,7 +215,7 @@ public void checkFileRead(Class<?> callerClass, Path path) {

@Override
public void checkFileRead(Class<?> callerClass, Path path, boolean followLinks) throws NoSuchFileException {
if (PolicyCheckerImpl.isPathOnDefaultFilesystem(path) == false) {
if (isPathOnDefaultFilesystem(path) == false) {
return;
}
var requestingClass = requestingClass(callerClass);
Expand Down Expand Up @@ -265,7 +263,7 @@ public void checkFileWrite(Class<?> callerClass, File file) {

@Override
public void checkFileWrite(Class<?> callerClass, Path path) {
if (PolicyCheckerImpl.isPathOnDefaultFilesystem(path) == false) {
if (isPathOnDefaultFilesystem(path) == false) {
return;
}
var requestingClass = requestingClass(callerClass);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
Expand Down Expand Up @@ -217,7 +219,7 @@ private static Set<Module> findSystemLayerModules() {
.filter(m -> SYSTEM_LAYER_MODULES.contains(m) == false)
.collect(Collectors.toUnmodifiableSet());

private final Map<String, Collection<Path>> pluginSourcePaths;
private final Function<String, Collection<Path>> pluginSourcePathsResolver;

/**
* Paths that are only allowed for a single module. Used to generate
Expand All @@ -231,7 +233,7 @@ public PolicyManager(
List<Entitlement> apmAgentEntitlements,
Map<String, Policy> pluginPolicies,
Function<Class<?>, PolicyScope> scopeResolver,
Map<String, Collection<Path>> pluginSourcePaths,
Function<String, Collection<Path>> pluginSourcePathsResolver,
PathLookup pathLookup
) {
this.serverEntitlements = buildScopeEntitlementsMap(requireNonNull(serverPolicy));
Expand All @@ -240,7 +242,7 @@ public PolicyManager(
.stream()
.collect(toUnmodifiableMap(Map.Entry::getKey, e -> buildScopeEntitlementsMap(e.getValue())));
this.scopeResolver = scopeResolver;
this.pluginSourcePaths = pluginSourcePaths;
this.pluginSourcePathsResolver = pluginSourcePathsResolver;
this.pathLookup = requireNonNull(pathLookup);

List<ExclusiveFileEntitlement> exclusiveFileEntitlements = new ArrayList<>();
Expand Down Expand Up @@ -334,7 +336,10 @@ protected final ModuleEntitlements computeEntitlements(Class<?> requestingClass)
default -> {
assert policyScope.kind() == PLUGIN;
var pluginEntitlements = pluginsEntitlements.get(componentName);
Collection<Path> componentPaths = pluginSourcePaths.getOrDefault(componentName, List.of());
Collection<Path> componentPaths = Objects.requireNonNullElse(
pluginSourcePathsResolver.apply(componentName),
Collections.emptyList()
);
if (pluginEntitlements == null) {
return defaultEntitlements(componentName, componentPaths, moduleName);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.net.URLClassLoader;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand Down Expand Up @@ -95,7 +96,7 @@ public void testGetEntitlements() {
List.of(),
Map.of("plugin1", new Policy("plugin1", List.of(new Scope("plugin.module1", List.of(new ExitVMEntitlement()))))),
c -> policyScope.get(),
Map.of("plugin1", plugin1SourcePaths),
Map.of("plugin1", plugin1SourcePaths)::get,
TEST_PATH_LOOKUP
);
Collection<Path> thisSourcePaths = policyManager.getComponentPathsFromClass(getClass());
Expand Down Expand Up @@ -170,7 +171,7 @@ public void testAgentsEntitlements() throws IOException, ClassNotFoundException
c -> c.getPackageName().startsWith(TEST_AGENTS_PACKAGE_NAME)
? PolicyScope.apmAgent("test.agent.module")
: PolicyScope.plugin("test", "test.plugin.module"),
Map.of(),
name -> Collections.emptyList(),
TEST_PATH_LOOKUP
);
ModuleEntitlements agentsEntitlements = policyManager.getEntitlements(TestAgent.class);
Expand All @@ -197,7 +198,7 @@ public void testDuplicateEntitlements() {
List.of(),
Map.of(),
c -> PolicyScope.plugin("test", moduleName(c)),
Map.of(),
name -> Collections.emptyList(),
TEST_PATH_LOOKUP
)
);
Expand All @@ -213,7 +214,7 @@ public void testDuplicateEntitlements() {
List.of(new CreateClassLoaderEntitlement(), new CreateClassLoaderEntitlement()),
Map.of(),
c -> PolicyScope.plugin("test", moduleName(c)),
Map.of(),
name -> Collections.emptyList(),
TEST_PATH_LOOKUP
)
);
Expand Down Expand Up @@ -249,7 +250,7 @@ public void testDuplicateEntitlements() {
)
),
c -> PolicyScope.plugin("plugin1", moduleName(c)),
Map.of("plugin1", List.of(Path.of("modules", "plugin1"))),
Map.of("plugin1", List.of(Path.of("modules", "plugin1")))::get,
TEST_PATH_LOOKUP
)
);
Expand Down Expand Up @@ -299,7 +300,7 @@ public void testFilesEntitlementsWithExclusive() {
)
),
c -> PolicyScope.plugin("", moduleName(c)),
Map.of("plugin1", List.of(Path.of("modules", "plugin1")), "plugin2", List.of(Path.of("modules", "plugin2"))),
Map.of("plugin1", List.of(Path.of("modules", "plugin1")), "plugin2", List.of(Path.of("modules", "plugin2")))::get,
TEST_PATH_LOOKUP
)
);
Expand Down Expand Up @@ -350,7 +351,7 @@ public void testFilesEntitlementsWithExclusive() {
)
),
c -> PolicyScope.plugin("", moduleName(c)),
Map.of(),
name -> Collections.emptyList(),
TEST_PATH_LOOKUP
)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,13 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import static java.util.stream.Collectors.toCollection;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;
import static org.elasticsearch.entitlement.runtime.policy.PathLookup.BaseDir.CONFIG;
import static org.elasticsearch.entitlement.runtime.policy.PathLookup.BaseDir.TEMP;
Expand Down Expand Up @@ -128,8 +126,6 @@ private static TestPolicyManager createPolicyManager(PathLookup pathLookup) thro
} else {
classPathEntries = Arrays.stream(classPathProperty.split(separator)).map(PathUtils::get).collect(toCollection(TreeSet::new));
}
Map<String, Collection<Path>> pluginSourcePaths = pluginNames.stream().collect(toMap(n -> n, n -> classPathEntries));

FilesEntitlementsValidation.validate(pluginPolicies, pathLookup);

String testOnlyPathString = System.getenv("es.entitlement.testOnlyPath");
Expand All @@ -148,8 +144,8 @@ private static TestPolicyManager createPolicyManager(PathLookup pathLookup) thro
HardcodedEntitlements.agentEntitlements(),
pluginPolicies,
scopeResolver,
pluginSourcePaths,
pathLookup,
classPathEntries,
testOnlyClassPath
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

package org.elasticsearch.entitlement.runtime.policy;

import org.apache.lucene.tests.mockfile.FilterFileSystem;

import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
Expand Down Expand Up @@ -37,4 +39,14 @@ public Stream<Path> resolveSettingPaths(BaseDir baseDir, String settingName) {
return Stream.empty();
}

@Override
public boolean isPathOnDefaultFilesystem(Path path) {
var fileSystem = path.getFileSystem();
if (fileSystem.getClass() != DEFAULT_FILESYSTEM_CLASS) {
while (fileSystem instanceof FilterFileSystem ffs) {
fileSystem = ffs.getDelegate();
}
}
return fileSystem.getClass() == DEFAULT_FILESYSTEM_CLASS;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,20 @@ public class TestPolicyManager extends PolicyManager {
* We need this larger map per class instead.
*/
final Map<Class<?>, ModuleEntitlements> classEntitlementsMap = new ConcurrentHashMap<>();

final Collection<Path> classpath;
final Collection<URI> testOnlyClasspath;

public TestPolicyManager(
Policy serverPolicy,
List<Entitlement> apmAgentEntitlements,
Map<String, Policy> pluginPolicies,
Function<Class<?>, PolicyScope> scopeResolver,
Map<String, Collection<Path>> pluginSourcePaths,
PathLookup pathLookup,
Collection<Path> classpath,
Collection<URI> testOnlyClasspath
) {
super(serverPolicy, apmAgentEntitlements, pluginPolicies, scopeResolver, pluginSourcePaths, pathLookup);
super(serverPolicy, apmAgentEntitlements, pluginPolicies, scopeResolver, name -> classpath, pathLookup);
this.classpath = classpath;
this.testOnlyClasspath = testOnlyClasspath;
reset();
}
Expand Down Expand Up @@ -118,6 +119,11 @@ boolean isTriviallyAllowed(Class<?> requestingClass) {
return super.isTriviallyAllowed(requestingClass);
}

@Override
protected Collection<Path> getComponentPathsFromClass(Class<?> requestingClass) {
return classpath; // required to grant read access to the production source and test resources
}

private boolean isEntitlementClass(Class<?> requestingClass) {
return requestingClass.getPackageName().startsWith("org.elasticsearch.entitlement")
&& (requestingClass.getName().contains("Test") == false);
Expand Down Expand Up @@ -180,6 +186,9 @@ private boolean isTestCode(Class<?> requestingClass) {
URI needle;
try {
needle = codeSource.getLocation().toURI();
if (needle.getScheme().equals("jrt")) {
return false; // won't be on testOnlyClasspath
}
} catch (URISyntaxException e) {
throw new IllegalStateException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ public void setupPolicyManager() {
List.of(),
Map.of(),
c -> new PolicyScope(PLUGIN, "example-plugin" + scopeCounter.incrementAndGet(), "org.example.module"),
Map.of(),
new TestPathLookup(Map.of()),
List.of(),
List.of()
);
policyManager.setActive(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import org.elasticsearch.core.PathUtils;
import org.elasticsearch.env.Environment;
import org.elasticsearch.env.TestEnvironment;
import org.elasticsearch.jdk.RuntimeVersionFeature;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.core.ssl.SSLService;
import org.junit.Before;
Expand Down Expand Up @@ -363,11 +362,6 @@ private void checkBlockedResource(
String configKey,
BiConsumer<String, Settings.Builder> configure
) throws Exception {
assumeTrue(
"Requires Security Manager to block access, entitlements are not checked for unit tests",
RuntimeVersionFeature.isSecurityManagerAvailable()
);

final String prefix = randomSslPrefix();
final Settings.Builder settings = Settings.builder();
configure.accept(prefix, settings);
Expand Down