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 06e985913c9b4..6a80ec75751c5 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 @@ -24,6 +24,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Map; +import java.util.Set; import java.util.function.Function; import java.util.stream.Stream; @@ -42,7 +43,8 @@ public record BootstrapArgs( Path libDir, Path logsDir, Path tempDir, - Path pidFile + Path pidFile, + Set> suppressFailureLogClasses ) { public BootstrapArgs { requireNonNull(pluginPolicies); @@ -58,6 +60,7 @@ public record BootstrapArgs( requireNonNull(libDir); requireNonNull(logsDir); requireNonNull(tempDir); + requireNonNull(suppressFailureLogClasses); } } @@ -82,6 +85,7 @@ public static BootstrapArgs bootstrapArgs() { * @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 + * @param suppressFailureLogClasses classes for which we do not need or want to log Entitlements failures */ public static void bootstrap( Map pluginPolicies, @@ -94,7 +98,8 @@ public static void bootstrap( Path libDir, Path logsDir, Path tempDir, - Path pidFile + Path pidFile, + Set> suppressFailureLogClasses ) { logger.debug("Loading entitlement agent"); if (EntitlementBootstrap.bootstrapArgs != null) { @@ -111,7 +116,8 @@ public static void bootstrap( libDir, logsDir, tempDir, - pidFile + pidFile, + suppressFailureLogClasses ); exportInitializationToAgent(); loadAgent(findAgentJar()); diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/initialization/EntitlementInitialization.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/initialization/EntitlementInitialization.java index 0fef2c27df663..f1142379c3fcc 100644 --- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/initialization/EntitlementInitialization.java +++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/initialization/EntitlementInitialization.java @@ -269,7 +269,8 @@ private static PolicyManager createPolicyManager() { resolver, AGENTS_PACKAGE_NAME, ENTITLEMENTS_MODULE, - pathLookup + pathLookup, + bootstrapArgs.suppressFailureLogClasses() ); } 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 b6296fe5d4713..cf3775474b79a 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 @@ -11,7 +11,6 @@ import org.elasticsearch.core.Strings; import org.elasticsearch.core.SuppressForbidden; -import org.elasticsearch.entitlement.bootstrap.EntitlementBootstrap; import org.elasticsearch.entitlement.instrumentation.InstrumentationService; import org.elasticsearch.entitlement.runtime.api.NotEntitledException; import org.elasticsearch.entitlement.runtime.policy.entitlements.CreateClassLoaderEntitlement; @@ -114,6 +113,7 @@ ModuleEntitlements policyEntitlements(String componentName, List en private final Function, String> pluginResolver; private final PathLookup pathLookup; private final FileAccessTree defaultFileAccess; + private final Set> mutedClasses; public static final String ALL_UNNAMED = "ALL-UNNAMED"; @@ -150,7 +150,8 @@ public PolicyManager( Function, String> pluginResolver, String apmAgentPackageName, Module entitlementsModule, - PathLookup pathLookup + PathLookup pathLookup, + Set> suppressFailureLogClasses ) { this.serverEntitlements = buildScopeEntitlementsMap(requireNonNull(serverPolicy)); this.apmAgentEntitlements = apmAgentEntitlements; @@ -162,6 +163,7 @@ public PolicyManager( this.entitlementsModule = entitlementsModule; this.pathLookup = requireNonNull(pathLookup); this.defaultFileAccess = FileAccessTree.of(FilesEntitlement.EMPTY, pathLookup); + this.mutedClasses = suppressFailureLogClasses; for (var e : serverEntitlements.entrySet()) { validateEntitlementsPerModule(SERVER_COMPONENT_NAME, e.getKey(), e.getValue()); @@ -386,7 +388,7 @@ public void checkAllNetworkAccess(Class callerClass) { checkFlagEntitlement(classEntitlements, OutboundNetworkEntitlement.class, requestingClass, callerClass); } - private static void checkFlagEntitlement( + private void checkFlagEntitlement( ModuleEntitlements classEntitlements, Class entitlementClass, Class requestingClass, @@ -446,10 +448,10 @@ public void checkWriteProperty(Class callerClass, String property) { ); } - private static void notEntitled(String message, Class callerClass) { + private void notEntitled(String message, Class callerClass) { var exception = new NotEntitledException(message); - // don't log self tests in EntitlementBootstrap - if (EntitlementBootstrap.class.equals(callerClass) == false) { + // Don't emit a log for muted classes, e.g. classes containing self tests + if (mutedClasses.contains(callerClass) == false) { logger.warn(message, exception); } throw exception; 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 c9fade1a48219..5a65ea81d0a0e 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 @@ -87,7 +87,8 @@ public void testGetEntitlementsThrowsOnMissingPluginUnnamedModule() { c -> "plugin1", TEST_AGENTS_PACKAGE_NAME, NO_ENTITLEMENTS_MODULE, - TEST_PATH_LOOKUP + TEST_PATH_LOOKUP, + Set.of() ); // Any class from the current module (unnamed) will do @@ -111,7 +112,8 @@ public void testGetEntitlementsThrowsOnMissingPolicyForPlugin() { c -> "plugin1", TEST_AGENTS_PACKAGE_NAME, NO_ENTITLEMENTS_MODULE, - TEST_PATH_LOOKUP + TEST_PATH_LOOKUP, + Set.of() ); // Any class from the current module (unnamed) will do @@ -131,7 +133,8 @@ public void testGetEntitlementsFailureIsCached() { c -> "plugin1", TEST_AGENTS_PACKAGE_NAME, NO_ENTITLEMENTS_MODULE, - TEST_PATH_LOOKUP + TEST_PATH_LOOKUP, + Set.of() ); // Any class from the current module (unnamed) will do @@ -156,7 +159,8 @@ public void testGetEntitlementsReturnsEntitlementsForPluginUnnamedModule() { c -> "plugin2", TEST_AGENTS_PACKAGE_NAME, NO_ENTITLEMENTS_MODULE, - TEST_PATH_LOOKUP + TEST_PATH_LOOKUP, + Set.of() ); // Any class from the current module (unnamed) will do @@ -174,7 +178,8 @@ public void testGetEntitlementsThrowsOnMissingPolicyForServer() throws ClassNotF c -> null, TEST_AGENTS_PACKAGE_NAME, NO_ENTITLEMENTS_MODULE, - TEST_PATH_LOOKUP + TEST_PATH_LOOKUP, + Set.of() ); // Tests do not run modular, so we cannot use a server class. @@ -204,7 +209,8 @@ public void testGetEntitlementsReturnsEntitlementsForServerModule() throws Class c -> null, TEST_AGENTS_PACKAGE_NAME, NO_ENTITLEMENTS_MODULE, - TEST_PATH_LOOKUP + TEST_PATH_LOOKUP, + Set.of() ); // Tests do not run modular, so we cannot use a server class. @@ -230,7 +236,8 @@ public void testGetEntitlementsReturnsEntitlementsForPluginModule() throws IOExc c -> "mock-plugin", TEST_AGENTS_PACKAGE_NAME, NO_ENTITLEMENTS_MODULE, - TEST_PATH_LOOKUP + TEST_PATH_LOOKUP, + Set.of() ); var layer = createLayerForJar(jar, "org.example.plugin"); @@ -249,7 +256,8 @@ public void testGetEntitlementsResultIsCached() { c -> "plugin2", TEST_AGENTS_PACKAGE_NAME, NO_ENTITLEMENTS_MODULE, - TEST_PATH_LOOKUP + TEST_PATH_LOOKUP, + Set.of() ); // Any class from the current module (unnamed) will do @@ -308,7 +316,8 @@ public void testAgentsEntitlements() throws IOException, ClassNotFoundException c -> c.getPackageName().startsWith(TEST_AGENTS_PACKAGE_NAME) ? null : "test", TEST_AGENTS_PACKAGE_NAME, NO_ENTITLEMENTS_MODULE, - TEST_PATH_LOOKUP + TEST_PATH_LOOKUP, + Set.of() ); ModuleEntitlements agentsEntitlements = policyManager.getEntitlements(TestAgent.class); assertThat(agentsEntitlements.hasEntitlement(CreateClassLoaderEntitlement.class), is(true)); @@ -336,7 +345,8 @@ public void testDuplicateEntitlements() { c -> "test", TEST_AGENTS_PACKAGE_NAME, NO_ENTITLEMENTS_MODULE, - TEST_PATH_LOOKUP + TEST_PATH_LOOKUP, + Set.of() ) ); assertEquals( @@ -353,7 +363,8 @@ public void testDuplicateEntitlements() { c -> "test", TEST_AGENTS_PACKAGE_NAME, NO_ENTITLEMENTS_MODULE, - TEST_PATH_LOOKUP + TEST_PATH_LOOKUP, + Set.of() ) ); assertEquals( @@ -387,7 +398,8 @@ public void testDuplicateEntitlements() { c -> "plugin1", TEST_AGENTS_PACKAGE_NAME, NO_ENTITLEMENTS_MODULE, - TEST_PATH_LOOKUP + TEST_PATH_LOOKUP, + Set.of() ) ); assertEquals( @@ -407,7 +419,8 @@ public void testPluginResolverOverridesAgents() { c -> "test", // Insist that the class is in a plugin TEST_AGENTS_PACKAGE_NAME, NO_ENTITLEMENTS_MODULE, - TEST_PATH_LOOKUP + TEST_PATH_LOOKUP, + Set.of() ); ModuleEntitlements notAgentsEntitlements = policyManager.getEntitlements(TestAgent.class); assertThat(notAgentsEntitlements.hasEntitlement(CreateClassLoaderEntitlement.class), is(false)); @@ -428,7 +441,8 @@ private static PolicyManager policyManager(String agentsPackageName, Module enti c -> "test", agentsPackageName, entitlementsModule, - TEST_PATH_LOOKUP + TEST_PATH_LOOKUP, + Set.of() ); } diff --git a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java index 05072ae940ae4..5ccb4af45ade2 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java @@ -256,9 +256,10 @@ private static void initPhase2(Bootstrap bootstrap) throws IOException { nodeEnv.libDir(), nodeEnv.logsDir(), nodeEnv.tmpDir(), - args.pidFile() + args.pidFile(), + Set.of(EntitlementSelfTester.class) ); - entitlementSelfTest(); + EntitlementSelfTester.entitlementSelfTest(); } else { assert RuntimeVersionFeature.isSecurityManagerAvailable(); // no need to explicitly enable native access for legacy code @@ -275,31 +276,33 @@ private static void initPhase2(Bootstrap bootstrap) throws IOException { bootstrap.setPluginsLoader(pluginsLoader); } - // check entitlements were loaded correctly. note this must be outside the entitlements lib. - private static void entitlementSelfTest() { - ensureCannotStartProcess(ProcessBuilder::start); - // Try again with reflection - ensureCannotStartProcess(Elasticsearch::reflectiveStartProcess); - } + private static class EntitlementSelfTester { + // check entitlements were loaded correctly. note this must be outside the entitlements lib. + private static void entitlementSelfTest() { + ensureCannotStartProcess(ProcessBuilder::start); + // Try again with reflection + ensureCannotStartProcess(EntitlementSelfTester::reflectiveStartProcess); + } - private static void ensureCannotStartProcess(CheckedConsumer startProcess) { - try { - // The command doesn't matter; it doesn't even need to exist - startProcess.accept(new ProcessBuilder("")); - } catch (NotEntitledException e) { - return; - } catch (Exception e) { - throw new IllegalStateException("Failed entitlement protection self-test", e); + private static void ensureCannotStartProcess(CheckedConsumer startProcess) { + try { + // The command doesn't matter; it doesn't even need to exist + startProcess.accept(new ProcessBuilder("")); + } catch (NotEntitledException e) { + return; + } catch (Exception e) { + throw new IllegalStateException("Failed entitlement protection self-test", e); + } + throw new IllegalStateException("Entitlement protection self-test was incorrectly permitted"); } - throw new IllegalStateException("Entitlement protection self-test was incorrectly permitted"); - } - private static void reflectiveStartProcess(ProcessBuilder pb) throws Exception { - try { - var start = ProcessBuilder.class.getMethod("start"); - start.invoke(pb); - } catch (InvocationTargetException e) { - throw (Exception) e.getCause(); + private static void reflectiveStartProcess(ProcessBuilder pb) throws Exception { + try { + var start = ProcessBuilder.class.getMethod("start"); + start.invoke(pb); + } catch (InvocationTargetException e) { + throw (Exception) e.getCause(); + } } }