Skip to content

[8.x] Refactor: ScopeResolver (#126921) (#127174) #127183

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 23, 2025
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 @@ -21,6 +21,7 @@
import org.elasticsearch.entitlement.runtime.policy.PathLookup;
import org.elasticsearch.entitlement.runtime.policy.PathLookupImpl;
import org.elasticsearch.entitlement.runtime.policy.Policy;
import org.elasticsearch.entitlement.runtime.policy.PolicyManager;
import org.elasticsearch.logging.LogManager;
import org.elasticsearch.logging.Logger;

Expand All @@ -39,14 +40,14 @@ public class EntitlementBootstrap {
public record BootstrapArgs(
@Nullable Policy serverPolicyPatch,
Map<String, Policy> pluginPolicies,
Function<Class<?>, String> pluginResolver,
Function<Class<?>, PolicyManager.PolicyScope> scopeResolver,
PathLookup pathLookup,
Map<String, Path> sourcePaths,
Set<Class<?>> suppressFailureLogClasses
) {
public BootstrapArgs {
requireNonNull(pluginPolicies);
requireNonNull(pluginResolver);
requireNonNull(scopeResolver);
requireNonNull(pathLookup);
requireNonNull(sourcePaths);
requireNonNull(suppressFailureLogClasses);
Expand All @@ -65,7 +66,7 @@ public static BootstrapArgs bootstrapArgs() {
*
* @param serverPolicyPatch a policy with additional entitlements to patch the embedded server layer policy
* @param pluginPolicies a map holding policies for plugins (and modules), by plugin (or module) name.
* @param pluginResolver a functor to map a Java Class to the plugin it belongs to (the plugin name).
* @param scopeResolver a functor to map a Java Class to the component and module it belongs to.
* @param settingResolver a functor to resolve a setting name pattern for one or more Elasticsearch settings.
* @param dataDirs data directories for Elasticsearch
* @param sharedRepoDirs shared repository directories for Elasticsearch
Expand All @@ -82,7 +83,7 @@ public static BootstrapArgs bootstrapArgs() {
public static void bootstrap(
Policy serverPolicyPatch,
Map<String, Policy> pluginPolicies,
Function<Class<?>, String> pluginResolver,
Function<Class<?>, PolicyManager.PolicyScope> scopeResolver,
Function<String, Stream<String>> settingResolver,
Path[] dataDirs,
Path[] sharedRepoDirs,
Expand All @@ -103,7 +104,7 @@ public static void bootstrap(
EntitlementBootstrap.bootstrapArgs = new BootstrapArgs(
serverPolicyPatch,
pluginPolicies,
pluginResolver,
scopeResolver,
new PathLookupImpl(
getUserHome(),
configDir,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@
*/
public class EntitlementInitialization {

private static final String AGENTS_PACKAGE_NAME = "co.elastic.apm.agent";
private static final Module ENTITLEMENTS_MODULE = PolicyManager.class.getModule();

private static ElasticsearchEntitlementChecker manager;
Expand Down Expand Up @@ -350,9 +349,8 @@ private static PolicyManager createPolicyManager() {
serverPolicy,
agentEntitlements,
pluginPolicies,
EntitlementBootstrap.bootstrapArgs().pluginResolver(),
EntitlementBootstrap.bootstrapArgs().scopeResolver(),
EntitlementBootstrap.bootstrapArgs().sourcePaths(),
AGENTS_PACKAGE_NAME,
ENTITLEMENTS_MODULE,
pathLookup,
bootstrapArgs.suppressFailureLogClasses()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@
import static java.util.zip.ZipFile.OPEN_READ;
import static org.elasticsearch.entitlement.bridge.Util.NO_CLASS;
import static org.elasticsearch.entitlement.runtime.policy.PathLookup.BaseDir.TEMP;
import static org.elasticsearch.entitlement.runtime.policy.PolicyManager.ComponentKind.APM_AGENT;
import static org.elasticsearch.entitlement.runtime.policy.PolicyManager.ComponentKind.PLUGIN;
import static org.elasticsearch.entitlement.runtime.policy.PolicyManager.ComponentKind.SERVER;
import static org.elasticsearch.entitlement.runtime.policy.PolicyManager.ComponentKind.UNKNOWN;

/**
* This class is responsible for finding the <strong>component</strong> (system, server, plugin, agent) for a caller class to check,
Expand Down Expand Up @@ -128,14 +132,55 @@ public class PolicyManager {
*/
private static final Logger generalLogger = LogManager.getLogger(PolicyManager.class);

static final String UNKNOWN_COMPONENT_NAME = "(unknown)";
static final String SERVER_COMPONENT_NAME = "(server)";
static final String APM_AGENT_COMPONENT_NAME = "(APM agent)";

static final Class<?> DEFAULT_FILESYSTEM_CLASS = PathUtils.getDefaultFileSystem().getClass();

static final Set<String> MODULES_EXCLUDED_FROM_SYSTEM_MODULES = Set.of("java.desktop");

/**
* Identifies a particular entitlement {@link Scope} within a {@link Policy}.
*/
public record PolicyScope(ComponentKind kind, String componentName, String moduleName) {
public PolicyScope {
requireNonNull(kind);
requireNonNull(componentName);
requireNonNull(moduleName);
assert kind.componentName == null || kind.componentName.equals(componentName);
}

public static PolicyScope unknown(String moduleName) {
return new PolicyScope(UNKNOWN, UNKNOWN.componentName, moduleName);
}

public static PolicyScope server(String moduleName) {
return new PolicyScope(SERVER, SERVER.componentName, moduleName);
}

public static PolicyScope apmAgent(String moduleName) {
return new PolicyScope(APM_AGENT, APM_AGENT.componentName, moduleName);
}

public static PolicyScope plugin(String componentName, String moduleName) {
return new PolicyScope(PLUGIN, componentName, moduleName);
}
}

public enum ComponentKind {
UNKNOWN("(unknown)"),
SERVER("(server)"),
APM_AGENT("(APM agent)"),
PLUGIN(null);

/**
* If this kind corresponds to a single component, this is that component's name;
* otherwise null.
*/
final String componentName;

ComponentKind(String componentName) {
this.componentName = componentName;
}
}

/**
* This class contains all the entitlements by type, plus the {@link FileAccessTree} for the special case of filesystem entitlements.
* <p>
Expand Down Expand Up @@ -209,7 +254,7 @@ ModuleEntitlements policyEntitlements(String componentName, Path componentPath,
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 Function<Class<?>, PolicyScope> scopeResolver;
private final PathLookup pathLookup;
private final Set<Class<?>> mutedClasses;

Expand Down Expand Up @@ -245,10 +290,6 @@ private static Set<Module> findSystemLayerModules() {
.collect(Collectors.toUnmodifiableSet());

private final Map<String, Path> sourcePaths;
/**
* The package name containing classes from the APM agent.
*/
private final String apmAgentPackageName;

/**
* Frames originating from this module are ignored in the permission logic.
Expand All @@ -266,9 +307,8 @@ public PolicyManager(
Policy serverPolicy,
List<Entitlement> apmAgentEntitlements,
Map<String, Policy> pluginPolicies,
Function<Class<?>, String> pluginResolver,
Function<Class<?>, PolicyScope> scopeResolver,
Map<String, Path> sourcePaths,
String apmAgentPackageName,
Module entitlementsModule,
PathLookup pathLookup,
Set<Class<?>> suppressFailureLogClasses
Expand All @@ -278,18 +318,17 @@ public PolicyManager(
this.pluginsEntitlements = requireNonNull(pluginPolicies).entrySet()
.stream()
.collect(toUnmodifiableMap(Map.Entry::getKey, e -> buildScopeEntitlementsMap(e.getValue())));
this.pluginResolver = pluginResolver;
this.scopeResolver = scopeResolver;
this.sourcePaths = sourcePaths;
this.apmAgentPackageName = apmAgentPackageName;
this.entitlementsModule = entitlementsModule;
this.pathLookup = requireNonNull(pathLookup);
this.mutedClasses = suppressFailureLogClasses;

List<ExclusiveFileEntitlement> exclusiveFileEntitlements = new ArrayList<>();
for (var e : serverEntitlements.entrySet()) {
validateEntitlementsPerModule(SERVER_COMPONENT_NAME, e.getKey(), e.getValue(), exclusiveFileEntitlements);
validateEntitlementsPerModule(SERVER.componentName, e.getKey(), e.getValue(), exclusiveFileEntitlements);
}
validateEntitlementsPerModule(APM_AGENT_COMPONENT_NAME, ALL_UNNAMED, apmAgentEntitlements, exclusiveFileEntitlements);
validateEntitlementsPerModule(APM_AGENT.componentName, ALL_UNNAMED, apmAgentEntitlements, exclusiveFileEntitlements);
for (var p : pluginsEntitlements.entrySet()) {
for (var m : p.getValue().entrySet()) {
validateEntitlementsPerModule(p.getKey(), m.getKey(), m.getValue(), exclusiveFileEntitlements);
Expand Down Expand Up @@ -684,50 +723,40 @@ ModuleEntitlements getEntitlements(Class<?> requestingClass) {
}

private ModuleEntitlements computeEntitlements(Class<?> requestingClass) {
Module requestingModule = requestingClass.getModule();
if (isServerModule(requestingModule)) {
return getModuleScopeEntitlements(
serverEntitlements,
requestingModule.getName(),
SERVER_COMPONENT_NAME,
getComponentPathFromClass(requestingClass)
);
}
var policyScope = scopeResolver.apply(requestingClass);
var componentName = policyScope.componentName();
var moduleName = policyScope.moduleName();

// plugins
var pluginName = pluginResolver.apply(requestingClass);
if (pluginName != null) {
var pluginEntitlements = pluginsEntitlements.get(pluginName);
if (pluginEntitlements == null) {
return defaultEntitlements(pluginName, sourcePaths.get(pluginName), requestingModule.getName());
} else {
switch (policyScope.kind()) {
case SERVER -> {
return getModuleScopeEntitlements(
pluginEntitlements,
getScopeName(requestingModule),
pluginName,
sourcePaths.get(pluginName)
serverEntitlements,
moduleName,
SERVER.componentName,
getComponentPathFromClass(requestingClass)
);
}
}

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

return defaultEntitlements(UNKNOWN_COMPONENT_NAME, null, requestingModule.getName());
}

private static String getScopeName(Module requestingModule) {
if (requestingModule.isNamed() == false) {
return ALL_UNNAMED;
} else {
return requestingModule.getName();
case APM_AGENT -> {
// The APM agent is the only thing running non-modular in the system classloader
return policyEntitlements(
APM_AGENT.componentName,
getComponentPathFromClass(requestingClass),
ALL_UNNAMED,
apmAgentEntitlements
);
}
case UNKNOWN -> {
return defaultEntitlements(UNKNOWN.componentName, null, moduleName);
}
default -> {
assert policyScope.kind() == PLUGIN;
var pluginEntitlements = pluginsEntitlements.get(componentName);
if (pluginEntitlements == null) {
return defaultEntitlements(componentName, sourcePaths.get(componentName), moduleName);
} else {
return getModuleScopeEntitlements(pluginEntitlements, moduleName, componentName, sourcePaths.get(componentName));
}
}
}
}

Expand Down Expand Up @@ -763,10 +792,6 @@ private ModuleEntitlements getModuleScopeEntitlements(
return policyEntitlements(componentName, componentPath, scopeName, entitlements);
}

private static boolean isServerModule(Module requestingModule) {
return requestingModule.isNamed() && requestingModule.getLayer() == ModuleLayer.boot();
}

/**
* Walks the stack to determine which class should be checked for entitlements.
*
Expand Down
Loading