diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/EntitlementBootstrap.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/EntitlementBootstrap.java index 928b953c295fe..3d364f4b53cec 100644 --- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/EntitlementBootstrap.java +++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/EntitlementBootstrap.java @@ -161,7 +161,7 @@ private static PolicyManager createPolicyManager( PathLookup pathLookup, Policy serverPolicyPatch, Function, PolicyManager.PolicyScope> scopeResolver, - Map> pluginSourcePaths + Map> pluginSourcePathsResolver ) { FilesEntitlementsValidation.validate(pluginPolicies, pathLookup); @@ -170,7 +170,7 @@ private static PolicyManager createPolicyManager( HardcodedEntitlements.agentEntitlements(), pluginPolicies, scopeResolver, - pluginSourcePaths, + pluginSourcePathsResolver::get, pathLookup ); } diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PathLookup.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PathLookup.java index 361d77ff83477..3d2daf5e407c6 100644 --- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PathLookup.java +++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PathLookup.java @@ -9,6 +9,8 @@ package org.elasticsearch.entitlement.runtime.policy; +import org.elasticsearch.core.PathUtils; + import java.nio.file.Path; import java.util.stream.Stream; @@ -16,6 +18,8 @@ * Resolves paths for known directories checked by entitlements. */ public interface PathLookup { + Class DEFAULT_FILESYSTEM_CLASS = PathUtils.getDefaultFileSystem().getClass(); + enum BaseDir { USER_HOME, CONFIG, @@ -37,4 +41,6 @@ enum BaseDir { * paths of the given {@code baseDir}. */ Stream resolveSettingPaths(BaseDir baseDir, String settingName); + + boolean isPathOnDefaultFilesystem(Path path); } diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PathLookupImpl.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PathLookupImpl.java index e3474250d43f0..df259254025bb 100644 --- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PathLookupImpl.java +++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PathLookupImpl.java @@ -75,4 +75,9 @@ public Stream 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; + } } diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyCheckerImpl.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyCheckerImpl.java index 2c3374f594847..97236e653f4ba 100644 --- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyCheckerImpl.java +++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyCheckerImpl.java @@ -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; @@ -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 suppressFailureLogPackages; /** * Frames originating from this module are ignored in the permission logic. @@ -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; @@ -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); @@ -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); diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java index f7d465adee631..8c2c55822b6b2 100644 --- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java +++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java @@ -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; @@ -217,7 +219,7 @@ private static Set findSystemLayerModules() { .filter(m -> SYSTEM_LAYER_MODULES.contains(m) == false) .collect(Collectors.toUnmodifiableSet()); - private final Map> pluginSourcePaths; + private final Function> pluginSourcePathsResolver; /** * Paths that are only allowed for a single module. Used to generate @@ -231,7 +233,7 @@ public PolicyManager( List apmAgentEntitlements, Map pluginPolicies, Function, PolicyScope> scopeResolver, - Map> pluginSourcePaths, + Function> pluginSourcePathsResolver, PathLookup pathLookup ) { this.serverEntitlements = buildScopeEntitlementsMap(requireNonNull(serverPolicy)); @@ -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 exclusiveFileEntitlements = new ArrayList<>(); @@ -334,7 +336,10 @@ protected final ModuleEntitlements computeEntitlements(Class requestingClass) default -> { assert policyScope.kind() == PLUGIN; var pluginEntitlements = pluginsEntitlements.get(componentName); - Collection componentPaths = pluginSourcePaths.getOrDefault(componentName, List.of()); + Collection componentPaths = Objects.requireNonNullElse( + pluginSourcePathsResolver.apply(componentName), + Collections.emptyList() + ); if (pluginEntitlements == null) { return defaultEntitlements(componentName, componentPaths, moduleName); } else { diff --git a/libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/PolicyManagerTests.java b/libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/PolicyManagerTests.java index 21197fc6bd942..e1f20a0eae990 100644 --- a/libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/PolicyManagerTests.java +++ b/libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/PolicyManagerTests.java @@ -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; @@ -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 thisSourcePaths = policyManager.getComponentPathsFromClass(getClass()); @@ -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); @@ -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 ) ); @@ -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 ) ); @@ -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 ) ); @@ -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 ) ); @@ -350,7 +351,7 @@ public void testFilesEntitlementsWithExclusive() { ) ), c -> PolicyScope.plugin("", moduleName(c)), - Map.of(), + name -> Collections.emptyList(), TEST_PATH_LOOKUP ) ); diff --git a/muted-tests.yml b/muted-tests.yml index d1d71b3a760a5..2f5068cf8ff3d 100644 --- a/muted-tests.yml +++ b/muted-tests.yml @@ -495,21 +495,6 @@ tests: - class: org.elasticsearch.test.rest.yaml.RcsCcsCommonYamlTestSuiteIT method: test {p0=msearch/20_typed_keys/Multisearch test with typed_keys parameter for sampler and significant terms} issue: https://github.com/elastic/elasticsearch/issues/130472 -- class: org.elasticsearch.xpack.ssl.SSLErrorMessageFileTests - method: testMessageForKeyStoreOutsideConfigDir - issue: https://github.com/elastic/elasticsearch/issues/127192 -- class: org.elasticsearch.xpack.ssl.SSLErrorMessageFileTests - method: testMessageForPemKeyOutsideConfigDir - issue: https://github.com/elastic/elasticsearch/issues/127192 -- class: org.elasticsearch.xpack.ssl.SSLErrorMessageFileTests - method: testMessageForPemCertificateOutsideConfigDir - issue: https://github.com/elastic/elasticsearch/issues/127192 -- class: org.elasticsearch.xpack.ssl.SSLErrorMessageFileTests - method: testMessageForTrustStoreOutsideConfigDir - issue: https://github.com/elastic/elasticsearch/issues/127192 -- class: org.elasticsearch.xpack.ssl.SSLErrorMessageFileTests - method: testMessageForCertificateAuthoritiesOutsideConfigDir - issue: https://github.com/elastic/elasticsearch/issues/127192 - class: org.elasticsearch.index.codec.vectors.cluster.HierarchicalKMeansTests method: testHKmeans issue: https://github.com/elastic/elasticsearch/issues/130497 diff --git a/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementBootstrap.java b/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementBootstrap.java index 160d601e3e585..423c407650434 100644 --- a/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementBootstrap.java +++ b/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementBootstrap.java @@ -34,7 +34,6 @@ 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; @@ -42,7 +41,6 @@ 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; @@ -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> pluginSourcePaths = pluginNames.stream().collect(toMap(n -> n, n -> classPathEntries)); - FilesEntitlementsValidation.validate(pluginPolicies, pathLookup); String testOnlyPathString = System.getenv("es.entitlement.testOnlyPath"); @@ -148,8 +144,8 @@ private static TestPolicyManager createPolicyManager(PathLookup pathLookup) thro HardcodedEntitlements.agentEntitlements(), pluginPolicies, scopeResolver, - pluginSourcePaths, pathLookup, + classPathEntries, testOnlyClassPath ); } diff --git a/test/framework/src/main/java/org/elasticsearch/entitlement/runtime/policy/TestPathLookup.java b/test/framework/src/main/java/org/elasticsearch/entitlement/runtime/policy/TestPathLookup.java index be99d8187f95e..458d83590758c 100644 --- a/test/framework/src/main/java/org/elasticsearch/entitlement/runtime/policy/TestPathLookup.java +++ b/test/framework/src/main/java/org/elasticsearch/entitlement/runtime/policy/TestPathLookup.java @@ -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; @@ -37,4 +39,14 @@ public Stream 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; + } } diff --git a/test/framework/src/main/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManager.java b/test/framework/src/main/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManager.java index 3d7387a6a2f3a..b504e51e119f7 100644 --- a/test/framework/src/main/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManager.java +++ b/test/framework/src/main/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManager.java @@ -38,7 +38,7 @@ public class TestPolicyManager extends PolicyManager { * We need this larger map per class instead. */ final Map, ModuleEntitlements> classEntitlementsMap = new ConcurrentHashMap<>(); - + final Collection classpath; final Collection testOnlyClasspath; public TestPolicyManager( @@ -46,11 +46,12 @@ public TestPolicyManager( List apmAgentEntitlements, Map pluginPolicies, Function, PolicyScope> scopeResolver, - Map> pluginSourcePaths, PathLookup pathLookup, + Collection classpath, Collection testOnlyClasspath ) { - super(serverPolicy, apmAgentEntitlements, pluginPolicies, scopeResolver, pluginSourcePaths, pathLookup); + super(serverPolicy, apmAgentEntitlements, pluginPolicies, scopeResolver, name -> classpath, pathLookup); + this.classpath = classpath; this.testOnlyClasspath = testOnlyClasspath; reset(); } @@ -118,6 +119,11 @@ boolean isTriviallyAllowed(Class requestingClass) { return super.isTriviallyAllowed(requestingClass); } + @Override + protected Collection 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); @@ -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); } diff --git a/test/framework/src/test/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManagerTests.java b/test/framework/src/test/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManagerTests.java index 366e0bd5505fe..4a62355f398d8 100644 --- a/test/framework/src/test/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManagerTests.java +++ b/test/framework/src/test/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManagerTests.java @@ -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); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/ssl/SSLErrorMessageFileTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/ssl/SSLErrorMessageFileTests.java index 4f64b780e1f97..2ac2d4ebf0c32 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/ssl/SSLErrorMessageFileTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/ssl/SSLErrorMessageFileTests.java @@ -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; @@ -363,11 +362,6 @@ private void checkBlockedResource( String configKey, BiConsumer 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);