Skip to content

Commit 8a6fa86

Browse files
committed
Merge branch '9.0' into backport/9.0/pr-121747
2 parents 0710a29 + 24b7b69 commit 8a6fa86

File tree

6 files changed

+174
-106
lines changed

6 files changed

+174
-106
lines changed

libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java

Lines changed: 74 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,35 @@
5050
public class PolicyManager {
5151
private static final Logger logger = LogManager.getLogger(PolicyManager.class);
5252

53-
record ModuleEntitlements(Map<Class<? extends Entitlement>, List<Entitlement>> entitlementsByType, FileAccessTree fileAccess) {
54-
public static final ModuleEntitlements NONE = new ModuleEntitlements(Map.of(), FileAccessTree.EMPTY);
53+
static final String UNKNOWN_COMPONENT_NAME = "(unknown)";
54+
static final String SERVER_COMPONENT_NAME = "(server)";
55+
static final String APM_AGENT_COMPONENT_NAME = "(APM agent)";
56+
57+
/**
58+
* @param componentName the plugin name; or else one of the special component names
59+
* like {@link #SERVER_COMPONENT_NAME} or {@link #APM_AGENT_COMPONENT_NAME}.
60+
*/
61+
record ModuleEntitlements(
62+
String componentName,
63+
Map<Class<? extends Entitlement>, List<Entitlement>> entitlementsByType,
64+
FileAccessTree fileAccess
65+
) {
5566

5667
ModuleEntitlements {
5768
entitlementsByType = Map.copyOf(entitlementsByType);
5869
}
5970

60-
public static ModuleEntitlements from(List<Entitlement> entitlements) {
71+
public static ModuleEntitlements none(String componentName) {
72+
return new ModuleEntitlements(componentName, Map.of(), FileAccessTree.EMPTY);
73+
}
74+
75+
public static ModuleEntitlements from(String componentName, List<Entitlement> entitlements) {
6176
var fileEntitlements = entitlements.stream()
6277
.filter(e -> e.getClass().equals(FileEntitlement.class))
6378
.map(e -> (FileEntitlement) e)
6479
.toList();
6580
return new ModuleEntitlements(
81+
componentName,
6682
entitlements.stream().collect(groupingBy(Entitlement::getClass)),
6783
FileAccessTree.of(fileEntitlements)
6884
);
@@ -84,7 +100,7 @@ public <E extends Entitlement> Stream<E> getEntitlements(Class<E> entitlementCla
84100
final Map<Module, ModuleEntitlements> moduleEntitlementsMap = new ConcurrentHashMap<>();
85101

86102
protected final Map<String, List<Entitlement>> serverEntitlements;
87-
protected final List<Entitlement> agentEntitlements;
103+
protected final List<Entitlement> apmAgentEntitlements;
88104
protected final Map<String, Map<String, List<Entitlement>>> pluginsEntitlements;
89105
private final Function<Class<?>, String> pluginResolver;
90106

@@ -106,9 +122,9 @@ private static Set<Module> findSystemModules() {
106122
}
107123

108124
/**
109-
* The package name containing agent classes.
125+
* The package name containing classes from the APM agent.
110126
*/
111-
private final String agentsPackageName;
127+
private final String apmAgentPackageName;
112128

113129
/**
114130
* Frames originating from this module are ignored in the permission logic.
@@ -117,25 +133,25 @@ private static Set<Module> findSystemModules() {
117133

118134
public PolicyManager(
119135
Policy serverPolicy,
120-
List<Entitlement> agentEntitlements,
136+
List<Entitlement> apmAgentEntitlements,
121137
Map<String, Policy> pluginPolicies,
122138
Function<Class<?>, String> pluginResolver,
123-
String agentsPackageName,
139+
String apmAgentPackageName,
124140
Module entitlementsModule
125141
) {
126142
this.serverEntitlements = buildScopeEntitlementsMap(requireNonNull(serverPolicy));
127-
this.agentEntitlements = agentEntitlements;
143+
this.apmAgentEntitlements = apmAgentEntitlements;
128144
this.pluginsEntitlements = requireNonNull(pluginPolicies).entrySet()
129145
.stream()
130146
.collect(toUnmodifiableMap(Map.Entry::getKey, e -> buildScopeEntitlementsMap(e.getValue())));
131147
this.pluginResolver = pluginResolver;
132-
this.agentsPackageName = agentsPackageName;
148+
this.apmAgentPackageName = apmAgentPackageName;
133149
this.entitlementsModule = entitlementsModule;
134150

135151
for (var e : serverEntitlements.entrySet()) {
136-
validateEntitlementsPerModule("server", e.getKey(), e.getValue());
152+
validateEntitlementsPerModule(SERVER_COMPONENT_NAME, e.getKey(), e.getValue());
137153
}
138-
validateEntitlementsPerModule("agent", "unnamed", agentEntitlements);
154+
validateEntitlementsPerModule(APM_AGENT_COMPONENT_NAME, "unnamed", apmAgentEntitlements);
139155
for (var p : pluginsEntitlements.entrySet()) {
140156
for (var m : p.getValue().entrySet()) {
141157
validateEntitlementsPerModule(p.getKey(), m.getKey(), m.getValue());
@@ -147,7 +163,7 @@ private static Map<String, List<Entitlement>> buildScopeEntitlementsMap(Policy p
147163
return policy.scopes().stream().collect(toUnmodifiableMap(Scope::moduleName, Scope::entitlements));
148164
}
149165

150-
private static void validateEntitlementsPerModule(String sourceName, String moduleName, List<Entitlement> entitlements) {
166+
private static void validateEntitlementsPerModule(String componentName, String moduleName, List<Entitlement> entitlements) {
151167
Set<Class<? extends Entitlement>> flagEntitlements = new HashSet<>();
152168
for (var e : entitlements) {
153169
if (e instanceof FileEntitlement) {
@@ -156,7 +172,7 @@ private static void validateEntitlementsPerModule(String sourceName, String modu
156172
if (flagEntitlements.contains(e.getClass())) {
157173
throw new IllegalArgumentException(
158174
"["
159-
+ sourceName
175+
+ componentName
160176
+ "] using module ["
161177
+ moduleName
162178
+ "] found duplicate flag entitlements ["
@@ -184,9 +200,10 @@ private void neverEntitled(Class<?> callerClass, Supplier<String> operationDescr
184200

185201
throw new NotEntitledException(
186202
Strings.format(
187-
"Not entitled: caller [%s], module [%s], operation [%s]",
188-
callerClass,
189-
requestingClass.getModule() == null ? "<none>" : requestingClass.getModule().getName(),
203+
"Not entitled: component [%s], module [%s], class [%s], operation [%s]",
204+
getEntitlements(requestingClass).componentName(),
205+
requestingClass.getModule().getName(),
206+
requestingClass,
190207
operationDescription.get()
191208
)
192209
);
@@ -240,9 +257,10 @@ public void checkFileRead(Class<?> callerClass, Path path) {
240257
if (entitlements.fileAccess().canRead(path) == false) {
241258
throw new NotEntitledException(
242259
Strings.format(
243-
"Not entitled: caller [%s], module [%s], entitlement [file], operation [read], path [%s]",
244-
callerClass,
260+
"Not entitled: component [%s], module [%s], class [%s], entitlement [file], operation [read], path [%s]",
261+
entitlements.componentName(),
245262
requestingClass.getModule(),
263+
requestingClass,
246264
path
247265
)
248266
);
@@ -264,9 +282,10 @@ public void checkFileWrite(Class<?> callerClass, Path path) {
264282
if (entitlements.fileAccess().canWrite(path) == false) {
265283
throw new NotEntitledException(
266284
Strings.format(
267-
"Not entitled: caller [%s], module [%s], entitlement [file], operation [write], path [%s]",
268-
callerClass,
285+
"Not entitled: component [%s], module [%s], class [%s], entitlement [file], operation [write], path [%s]",
286+
entitlements.componentName(),
269287
requestingClass.getModule(),
288+
requestingClass,
270289
path
271290
)
272291
);
@@ -300,30 +319,33 @@ public void checkAllNetworkAccess(Class<?> callerClass) {
300319
}
301320

302321
var classEntitlements = getEntitlements(requestingClass);
303-
if (classEntitlements.hasEntitlement(InboundNetworkEntitlement.class) == false) {
304-
throw new NotEntitledException(
305-
Strings.format(
306-
"Missing entitlement: class [%s], module [%s], entitlement [inbound_network]",
307-
requestingClass,
308-
requestingClass.getModule().getName()
309-
)
310-
);
311-
}
322+
checkFlagEntitlement(classEntitlements, InboundNetworkEntitlement.class, requestingClass);
323+
checkFlagEntitlement(classEntitlements, OutboundNetworkEntitlement.class, requestingClass);
324+
}
312325

313-
if (classEntitlements.hasEntitlement(OutboundNetworkEntitlement.class) == false) {
326+
private static void checkFlagEntitlement(
327+
ModuleEntitlements classEntitlements,
328+
Class<? extends Entitlement> entitlementClass,
329+
Class<?> requestingClass
330+
) {
331+
if (classEntitlements.hasEntitlement(entitlementClass) == false) {
314332
throw new NotEntitledException(
315333
Strings.format(
316-
"Missing entitlement: class [%s], module [%s], entitlement [outbound_network]",
334+
"Not entitled: component [%s], module [%s], class [%s], entitlement [%s]",
335+
classEntitlements.componentName(),
336+
requestingClass.getModule().getName(),
317337
requestingClass,
318-
requestingClass.getModule().getName()
338+
PolicyParser.getEntitlementTypeName(entitlementClass)
319339
)
320340
);
321341
}
322342
logger.debug(
323343
() -> Strings.format(
324-
"Entitled: class [%s], module [%s], entitlements [inbound_network, outbound_network]",
344+
"Entitled: component [%s], module [%s], class [%s], entitlement [%s]",
345+
classEntitlements.componentName(),
346+
requestingClass.getModule().getName(),
325347
requestingClass,
326-
requestingClass.getModule().getName()
348+
PolicyParser.getEntitlementTypeName(entitlementClass)
327349
)
328350
);
329351
}
@@ -338,19 +360,21 @@ public void checkWriteProperty(Class<?> callerClass, String property) {
338360
if (entitlements.getEntitlements(WriteSystemPropertiesEntitlement.class).anyMatch(e -> e.properties().contains(property))) {
339361
logger.debug(
340362
() -> Strings.format(
341-
"Entitled: class [%s], module [%s], entitlement [write_system_properties], property [%s]",
342-
requestingClass,
363+
"Entitled: component [%s], module [%s], class [%s], entitlement [write_system_properties], property [%s]",
364+
entitlements.componentName(),
343365
requestingClass.getModule().getName(),
366+
requestingClass,
344367
property
345368
)
346369
);
347370
return;
348371
}
349372
throw new NotEntitledException(
350373
Strings.format(
351-
"Missing entitlement: class [%s], module [%s], entitlement [write_system_properties], property [%s]",
352-
requestingClass,
374+
"Not entitled: component [%s], module [%s], class [%s], entitlement [write_system_properties], property [%s]",
375+
entitlements.componentName(),
353376
requestingClass.getModule().getName(),
377+
requestingClass,
354378
property
355379
)
356380
);
@@ -361,27 +385,7 @@ private void checkEntitlementPresent(Class<?> callerClass, Class<? extends Entit
361385
if (isTriviallyAllowed(requestingClass)) {
362386
return;
363387
}
364-
365-
ModuleEntitlements entitlements = getEntitlements(requestingClass);
366-
if (entitlements.hasEntitlement(entitlementClass)) {
367-
logger.debug(
368-
() -> Strings.format(
369-
"Entitled: class [%s], module [%s], entitlement [%s]",
370-
requestingClass,
371-
requestingClass.getModule().getName(),
372-
PolicyParser.getEntitlementTypeName(entitlementClass)
373-
)
374-
);
375-
return;
376-
}
377-
throw new NotEntitledException(
378-
Strings.format(
379-
"Missing entitlement: class [%s], module [%s], entitlement [%s]",
380-
requestingClass,
381-
requestingClass.getModule().getName(),
382-
PolicyParser.getEntitlementTypeName(entitlementClass)
383-
)
384-
);
388+
checkFlagEntitlement(getEntitlements(requestingClass), entitlementClass, requestingClass);
385389
}
386390

387391
ModuleEntitlements getEntitlements(Class<?> requestingClass) {
@@ -391,47 +395,44 @@ ModuleEntitlements getEntitlements(Class<?> requestingClass) {
391395
private ModuleEntitlements computeEntitlements(Class<?> requestingClass) {
392396
Module requestingModule = requestingClass.getModule();
393397
if (isServerModule(requestingModule)) {
394-
return getModuleScopeEntitlements(requestingClass, serverEntitlements, requestingModule.getName(), "server");
398+
return getModuleScopeEntitlements(serverEntitlements, requestingModule.getName(), SERVER_COMPONENT_NAME);
395399
}
396400

397401
// plugins
398402
var pluginName = pluginResolver.apply(requestingClass);
399403
if (pluginName != null) {
400404
var pluginEntitlements = pluginsEntitlements.get(pluginName);
401405
if (pluginEntitlements == null) {
402-
return ModuleEntitlements.NONE;
406+
return ModuleEntitlements.none(pluginName);
403407
} else {
404408
final String scopeName;
405409
if (requestingModule.isNamed() == false) {
406410
scopeName = ALL_UNNAMED;
407411
} else {
408412
scopeName = requestingModule.getName();
409413
}
410-
return getModuleScopeEntitlements(requestingClass, pluginEntitlements, scopeName, pluginName);
414+
return getModuleScopeEntitlements(pluginEntitlements, scopeName, pluginName);
411415
}
412416
}
413417

414-
if (requestingModule.isNamed() == false && requestingClass.getPackageName().startsWith(agentsPackageName)) {
415-
// agents are the only thing running non-modular in the system classloader
416-
return ModuleEntitlements.from(agentEntitlements);
418+
if (requestingModule.isNamed() == false && requestingClass.getPackageName().startsWith(apmAgentPackageName)) {
419+
// The APM agent is the only thing running non-modular in the system classloader
420+
return ModuleEntitlements.from(APM_AGENT_COMPONENT_NAME, apmAgentEntitlements);
417421
}
418422

419-
logger.warn("No applicable entitlement policy for class [{}]", requestingClass.getName());
420-
return ModuleEntitlements.NONE;
423+
return ModuleEntitlements.none(UNKNOWN_COMPONENT_NAME);
421424
}
422425

423426
private ModuleEntitlements getModuleScopeEntitlements(
424-
Class<?> callerClass,
425427
Map<String, List<Entitlement>> scopeEntitlements,
426428
String moduleName,
427-
String component
429+
String componentName
428430
) {
429431
var entitlements = scopeEntitlements.get(moduleName);
430432
if (entitlements == null) {
431-
logger.warn("No applicable entitlement policy for [{}], module [{}], class [{}]", component, moduleName, callerClass);
432-
return ModuleEntitlements.NONE;
433+
return ModuleEntitlements.none(componentName);
433434
}
434-
return ModuleEntitlements.from(entitlements);
435+
return ModuleEntitlements.from(componentName, entitlements);
435436
}
436437

437438
private static boolean isServerModule(Module requestingModule) {

0 commit comments

Comments
 (0)