Skip to content

Commit 1bb1629

Browse files
authored
Merge branch 'main' into entitlements/fix_fips_entitlement_trigger
2 parents ca93348 + d844c6a commit 1bb1629

File tree

36 files changed

+1144
-217
lines changed

36 files changed

+1144
-217
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.gradle.transform;
11+
12+
import org.gradle.api.Action;
13+
import org.gradle.api.artifacts.dsl.DependencyHandler;
14+
import org.gradle.api.artifacts.transform.InputArtifact;
15+
import org.gradle.api.artifacts.transform.TransformAction;
16+
import org.gradle.api.artifacts.transform.TransformOutputs;
17+
import org.gradle.api.artifacts.transform.TransformParameters;
18+
import org.gradle.api.artifacts.type.ArtifactTypeDefinition;
19+
import org.gradle.api.file.FileSystemLocation;
20+
import org.gradle.api.provider.Provider;
21+
import org.gradle.api.tasks.Input;
22+
23+
import java.io.BufferedOutputStream;
24+
import java.io.File;
25+
import java.io.FileOutputStream;
26+
import java.io.IOException;
27+
import java.io.Serializable;
28+
import java.io.UncheckedIOException;
29+
import java.nio.file.FileSystems;
30+
import java.nio.file.Path;
31+
import java.nio.file.PathMatcher;
32+
import java.util.ArrayList;
33+
import java.util.Enumeration;
34+
import java.util.List;
35+
import java.util.zip.ZipEntry;
36+
import java.util.zip.ZipFile;
37+
import java.util.zip.ZipOutputStream;
38+
39+
public abstract class FilteringJarTransform implements TransformAction<FilteringJarTransform.Parameters> {
40+
public static final String FILTERED_JAR_TYPE = "filtered-jar";
41+
42+
@InputArtifact
43+
public abstract Provider<FileSystemLocation> getInputArtifact();
44+
45+
@Override
46+
public void transform(TransformOutputs outputs) {
47+
File original = getInputArtifact().get().getAsFile();
48+
File transformed = outputs.file(original.getName());
49+
List<PathMatcher> excludes = createMatchers(getParameters().getExcludes());
50+
51+
try (
52+
ZipFile input = new ZipFile(original);
53+
ZipOutputStream output = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(transformed)))
54+
) {
55+
Enumeration<? extends ZipEntry> entries = input.entries();
56+
while (entries.hasMoreElements()) {
57+
ZipEntry entry = entries.nextElement();
58+
if (excludes.stream().noneMatch(e -> e.matches(Path.of(entry.getName())))) {
59+
output.putNextEntry(entry);
60+
input.getInputStream(entry).transferTo(output);
61+
output.closeEntry();
62+
}
63+
}
64+
65+
output.flush();
66+
output.finish();
67+
} catch (IOException e) {
68+
throw new UncheckedIOException("Failed to patch archive", e);
69+
}
70+
}
71+
72+
private List<PathMatcher> createMatchers(List<String> patterns) {
73+
return patterns.stream().map(p -> FileSystems.getDefault().getPathMatcher("glob:" + p)).toList();
74+
}
75+
76+
public static void registerTransform(DependencyHandler dependencyHandler, Action<Parameters> config) {
77+
dependencyHandler.registerTransform(FilteringJarTransform.class, spec -> {
78+
spec.getFrom().attribute(ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE, ArtifactTypeDefinition.JAR_TYPE);
79+
spec.getTo().attribute(ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE, FILTERED_JAR_TYPE);
80+
config.execute(spec.getParameters());
81+
});
82+
}
83+
84+
public abstract static class Parameters implements TransformParameters, Serializable {
85+
private List<String> excludes = new ArrayList<>();
86+
87+
@Input
88+
public List<String> getExcludes() {
89+
return excludes;
90+
}
91+
92+
public void exclude(String exclude) {
93+
excludes.add(exclude);
94+
}
95+
}
96+
}

distribution/build.gradle

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import org.elasticsearch.gradle.internal.ConcatFilesTask
1515
import org.elasticsearch.gradle.internal.DependenciesInfoPlugin
1616
import org.elasticsearch.gradle.internal.NoticeTask
1717
import org.elasticsearch.gradle.internal.test.ClusterFeaturesMetadataPlugin
18+
import org.elasticsearch.gradle.transform.FilteringJarTransform
1819

1920
import java.nio.file.Files
2021
import java.nio.file.Path
@@ -261,7 +262,7 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) {
261262
* Properties to expand when copying packaging files *
262263
*****************************************************************************/
263264
configurations {
264-
['libs', 'libsVersionChecker', 'libsCliLauncher', 'libsServerCli', 'libsWindowsServiceCli', 'libsPluginCli', 'libsKeystoreCli', 'libsSecurityCli', 'libsGeoIpCli', 'libsAnsiConsole', 'libsNative', 'libsEntitlementAgent', 'libsEntitlementBridge'].each {
265+
['libs', 'libsVersionChecker', 'libsCliLauncher', 'libsServerCli', 'libsWindowsServiceCli', 'libsPluginCli', 'libsKeystoreCli', 'libsSecurityCli', 'libsGeoIpCli', 'libsAnsiConsole', 'libsNative', 'libsEntitlementAgent'].each {
265266
create(it) {
266267
canBeConsumed = false
267268
canBeResolved = true
@@ -272,12 +273,28 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) {
272273
}
273274
}
274275
}
276+
libsEntitlementBridge {
277+
canBeConsumed = false
278+
canBeResolved = true
279+
attributes {
280+
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY))
281+
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
282+
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL))
283+
attribute(ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE, FilteringJarTransform.FILTERED_JAR_TYPE)
284+
}
285+
}
275286
all {
276287
resolutionStrategy.dependencySubstitution {
277288
substitute module("org.apache.logging.log4j:log4j-core") using project(":libs:log4j") because "patched to remove JndiLookup class"}
278289
}
279290
}
280291

292+
// Register artifact transform for filtering entitlements-bridge jar
293+
FilteringJarTransform.registerTransform(dependencies) { spec ->
294+
spec.exclude('module-info.class')
295+
spec.exclude('META-INF/versions/**')
296+
}
297+
281298
dependencies {
282299
libs project(':server')
283300

distribution/docker/src/docker/iron_bank/hardening_manifest.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ maintainers:
4747
- name: "Mark Vieira"
4848
4949
username: "mark-vieira"
50-
- name: "Rene Gröschke"
50+
- name: "Rene Groeschke"
5151
5252
username: "breskeby"
5353
- email: "[email protected]"

docs/changelog/122991.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 122991
2+
summary: "GCS blob store: add `OperationPurpose/Operation` stats counters"
3+
area: Snapshot/Restore
4+
type: enhancement
5+
issues: []

docs/changelog/123381.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 123381
2+
summary: Push down `StartsWith` and `EndsWith` functions to Lucene
3+
area: ES|QL
4+
type: enhancement
5+
issues:
6+
- 123067

docs/changelog/123460.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 123460
2+
summary: "ES|QL: Support `::date` in inline cast"
3+
area: ES|QL
4+
type: enhancement
5+
issues:
6+
- 116746

docs/reference/esql/functions/kibana/inline_cast.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

libs/entitlement/qa/entitlement-test-plugin/src/main/java/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@
1818
requires java.logging;
1919
requires java.net.http;
2020
requires jdk.net;
21+
requires java.desktop;
2122
}

libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/FileCheckActions.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
import java.util.zip.ZipException;
3636
import java.util.zip.ZipFile;
3737

38+
import javax.imageio.stream.FileImageInputStream;
39+
3840
import static java.nio.charset.Charset.defaultCharset;
3941
import static java.nio.file.StandardOpenOption.CREATE;
4042
import static java.nio.file.StandardOpenOption.WRITE;
@@ -561,5 +563,13 @@ static void httpResponseBodySubscribersOfFile_FileOpenOptions_readOnly() {
561563
HttpResponse.BodySubscribers.ofFile(readFile(), CREATE, WRITE);
562564
}
563565

566+
@EntitlementTest(expectedAccess = ALWAYS_DENIED)
567+
static void javaDesktopFileAccess() throws Exception {
568+
// Test file access from a java.desktop class. We explicitly exclude that module from the "system modules", so we expect
569+
// any sensitive operation from java.desktop to fail.
570+
var file = EntitledActions.createTempFileForRead();
571+
new FileImageInputStream(file.toFile()).close();
572+
}
573+
564574
private FileCheckActions() {}
565575
}

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

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ public class PolicyManager {
6767

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

70+
static final Set<String> MODULES_EXCLUDED_FROM_SYSTEM_MODULES = Set.of("java.desktop");
71+
7072
/**
7173
* @param componentName the plugin name; or else one of the special component names
7274
* like {@link #SERVER_COMPONENT_NAME} or {@link #APM_AGENT_COMPONENT_NAME}.
@@ -141,7 +143,13 @@ private static Set<Module> findSystemModules() {
141143
// entitlements is a "system" module, we can do anything from it
142144
Stream.of(PolicyManager.class.getModule()),
143145
// anything in the boot layer is also part of the system
144-
ModuleLayer.boot().modules().stream().filter(m -> systemModulesDescriptors.contains(m.getDescriptor()))
146+
ModuleLayer.boot()
147+
.modules()
148+
.stream()
149+
.filter(
150+
m -> systemModulesDescriptors.contains(m.getDescriptor())
151+
&& MODULES_EXCLUDED_FROM_SYSTEM_MODULES.contains(m.getName()) == false
152+
)
145153
).collect(Collectors.toUnmodifiableSet());
146154
}
147155

@@ -247,15 +255,17 @@ private void neverEntitled(Class<?> callerClass, Supplier<String> operationDescr
247255
return;
248256
}
249257

258+
String componentName = getEntitlements(requestingClass).componentName();
250259
notEntitled(
251260
Strings.format(
252-
"Not entitled: component [%s], module [%s], class [%s], operation [%s]",
253-
getEntitlements(requestingClass).componentName(),
261+
"component [%s], module [%s], class [%s], operation [%s]",
262+
componentName,
254263
requestingClass.getModule().getName(),
255264
requestingClass,
256265
operationDescription.get()
257266
),
258-
callerClass
267+
callerClass,
268+
componentName
259269
);
260270
}
261271

@@ -366,13 +376,14 @@ public void checkFileRead(Class<?> callerClass, Path path, boolean followLinks)
366376
if (canRead == false) {
367377
notEntitled(
368378
Strings.format(
369-
"Not entitled: component [%s], module [%s], class [%s], entitlement [file], operation [read], path [%s]",
379+
"component [%s], module [%s], class [%s], entitlement [file], operation [read], path [%s]",
370380
entitlements.componentName(),
371381
requestingClass.getModule().getName(),
372382
requestingClass,
373383
realPath == null ? path : Strings.format("%s -> %s", path, realPath)
374384
),
375-
callerClass
385+
callerClass,
386+
entitlements.componentName()
376387
);
377388
}
378389
}
@@ -395,13 +406,14 @@ public void checkFileWrite(Class<?> callerClass, Path path) {
395406
if (entitlements.fileAccess().canWrite(path) == false) {
396407
notEntitled(
397408
Strings.format(
398-
"Not entitled: component [%s], module [%s], class [%s], entitlement [file], operation [write], path [%s]",
409+
"component [%s], module [%s], class [%s], entitlement [file], operation [write], path [%s]",
399410
entitlements.componentName(),
400411
requestingClass.getModule().getName(),
401412
requestingClass,
402413
path
403414
),
404-
callerClass
415+
callerClass,
416+
entitlements.componentName()
405417
);
406418
}
407419
}
@@ -483,13 +495,14 @@ private void checkFlagEntitlement(
483495
if (classEntitlements.hasEntitlement(entitlementClass) == false) {
484496
notEntitled(
485497
Strings.format(
486-
"Not entitled: component [%s], module [%s], class [%s], entitlement [%s]",
498+
"component [%s], module [%s], class [%s], entitlement [%s]",
487499
classEntitlements.componentName(),
488500
requestingClass.getModule().getName(),
489501
requestingClass,
490502
PolicyParser.getEntitlementTypeName(entitlementClass)
491503
),
492-
callerClass
504+
callerClass,
505+
classEntitlements.componentName()
493506
);
494507
}
495508
logger.debug(
@@ -524,21 +537,29 @@ public void checkWriteProperty(Class<?> callerClass, String property) {
524537
}
525538
notEntitled(
526539
Strings.format(
527-
"Not entitled: component [%s], module [%s], class [%s], entitlement [write_system_properties], property [%s]",
540+
"component [%s], module [%s], class [%s], entitlement [write_system_properties], property [%s]",
528541
entitlements.componentName(),
529542
requestingClass.getModule().getName(),
530543
requestingClass,
531544
property
532545
),
533-
callerClass
546+
callerClass,
547+
entitlements.componentName()
534548
);
535549
}
536550

537-
private void notEntitled(String message, Class<?> callerClass) {
551+
private void notEntitled(String message, Class<?> callerClass, String componentName) {
538552
var exception = new NotEntitledException(message);
539553
// Don't emit a log for muted classes, e.g. classes containing self tests
540554
if (mutedClasses.contains(callerClass) == false) {
541-
logger.warn(message, exception);
555+
var moduleName = callerClass.getModule().getName();
556+
var loggerSuffix = "." + componentName + "." + ((moduleName == null) ? ALL_UNNAMED : moduleName);
557+
var notEntitledLogger = LogManager.getLogger(PolicyManager.class.getName() + loggerSuffix);
558+
String frameInfoSuffix = StackWalker.getInstance(RETAIN_CLASS_REFERENCE)
559+
.walk(this::findRequestingFrame)
560+
.map(frame -> "\n\tat " + frame)
561+
.orElse("");
562+
notEntitledLogger.warn("Not entitled: " + message + frameInfoSuffix);
542563
}
543564
throw exception;
544565
}
@@ -658,19 +679,18 @@ Class<?> requestingClass(Class<?> callerClass) {
658679
return callerClass;
659680
}
660681
Optional<Class<?>> result = StackWalker.getInstance(RETAIN_CLASS_REFERENCE)
661-
.walk(frames -> findRequestingClass(frames.map(StackFrame::getDeclaringClass)));
682+
.walk(frames -> findRequestingFrame(frames).map(StackFrame::getDeclaringClass));
662683
return result.orElse(null);
663684
}
664685

665686
/**
666-
* Given a stream of classes corresponding to the frames from a {@link StackWalker},
667-
* returns the module whose entitlements should be checked.
687+
* Given a stream of {@link StackFrame}s, identify the one whose entitlements should be checked.
668688
*
669689
* @throws NullPointerException if the requesting module is {@code null}
670690
*/
671-
Optional<Class<?>> findRequestingClass(Stream<Class<?>> classes) {
672-
return classes.filter(c -> c.getModule() != entitlementsModule) // Ignore the entitlements library
673-
.skip(1) // Skip the sensitive caller method
691+
Optional<StackFrame> findRequestingFrame(Stream<StackFrame> frames) {
692+
return frames.filter(f -> f.getDeclaringClass().getModule() != entitlementsModule) // ignore entitlements library
693+
.skip(1) // Skip the sensitive caller method
674694
.findFirst();
675695
}
676696

0 commit comments

Comments
 (0)