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 @@ -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;

Expand All @@ -42,7 +43,8 @@ public record BootstrapArgs(
Path libDir,
Path logsDir,
Path tempDir,
Path pidFile
Path pidFile,
Set<Class<?>> suppressFailureLogClasses
) {
public BootstrapArgs {
requireNonNull(pluginPolicies);
Expand All @@ -58,6 +60,7 @@ public record BootstrapArgs(
requireNonNull(libDir);
requireNonNull(logsDir);
requireNonNull(tempDir);
requireNonNull(suppressFailureLogClasses);
}
}

Expand All @@ -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<String, Policy> pluginPolicies,
Expand All @@ -94,7 +98,8 @@ public static void bootstrap(
Path libDir,
Path logsDir,
Path tempDir,
Path pidFile
Path pidFile,
Set<Class<?>> suppressFailureLogClasses
) {
logger.debug("Loading entitlement agent");
if (EntitlementBootstrap.bootstrapArgs != null) {
Expand All @@ -111,7 +116,8 @@ public static void bootstrap(
libDir,
logsDir,
tempDir,
pidFile
pidFile,
suppressFailureLogClasses
);
exportInitializationToAgent();
loadAgent(findAgentJar());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,8 @@ private static PolicyManager createPolicyManager() {
resolver,
AGENTS_PACKAGE_NAME,
ENTITLEMENTS_MODULE,
pathLookup
pathLookup,
bootstrapArgs.suppressFailureLogClasses()
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -114,6 +113,7 @@ ModuleEntitlements policyEntitlements(String componentName, List<Entitlement> en
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 Down Expand Up @@ -150,7 +150,8 @@ public PolicyManager(
Function<Class<?>, String> pluginResolver,
String apmAgentPackageName,
Module entitlementsModule,
PathLookup pathLookup
PathLookup pathLookup,
Set<Class<?>> suppressFailureLogClasses
) {
this.serverEntitlements = buildScopeEntitlementsMap(requireNonNull(serverPolicy));
this.apmAgentEntitlements = apmAgentEntitlements;
Expand All @@ -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());
Expand Down Expand Up @@ -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<? extends Entitlement> entitlementClass,
Class<?> requestingClass,
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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.
Expand All @@ -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");
Expand All @@ -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
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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(
Expand All @@ -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(
Expand Down Expand Up @@ -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(
Expand All @@ -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));
Expand All @@ -428,7 +441,8 @@ private static PolicyManager policyManager(String agentsPackageName, Module enti
c -> "test",
agentsPackageName,
entitlementsModule,
TEST_PATH_LOOKUP
TEST_PATH_LOOKUP,
Set.of()
);
}

Expand Down
51 changes: 27 additions & 24 deletions server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<ProcessBuilder, ?> 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<ProcessBuilder, ?> 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();
}
}
}

Expand Down