mirror of
https://github.com/elastic/elasticsearch.git
synced 2025-06-28 09:28:55 -04:00
[Entitlements] Instrument nio path (#122507)
This commit is contained in:
parent
780cac5a6d
commit
7fd1addccf
6 changed files with 185 additions and 26 deletions
|
@ -58,6 +58,8 @@ import java.nio.file.FileStore;
|
||||||
import java.nio.file.LinkOption;
|
import java.nio.file.LinkOption;
|
||||||
import java.nio.file.OpenOption;
|
import java.nio.file.OpenOption;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.WatchEvent;
|
||||||
|
import java.nio.file.WatchService;
|
||||||
import java.nio.file.attribute.FileAttribute;
|
import java.nio.file.attribute.FileAttribute;
|
||||||
import java.nio.file.attribute.UserPrincipal;
|
import java.nio.file.attribute.UserPrincipal;
|
||||||
import java.nio.file.spi.FileSystemProvider;
|
import java.nio.file.spi.FileSystemProvider;
|
||||||
|
@ -654,6 +656,19 @@ public interface EntitlementChecker {
|
||||||
|
|
||||||
void checkType(Class<?> callerClass, FileStore that);
|
void checkType(Class<?> callerClass, FileStore that);
|
||||||
|
|
||||||
|
// path
|
||||||
|
void checkPathToRealPath(Class<?> callerClass, Path that, LinkOption... options);
|
||||||
|
|
||||||
|
void checkPathRegister(Class<?> callerClass, Path that, WatchService watcher, WatchEvent.Kind<?>... events);
|
||||||
|
|
||||||
|
void checkPathRegister(
|
||||||
|
Class<?> callerClass,
|
||||||
|
Path that,
|
||||||
|
WatchService watcher,
|
||||||
|
WatchEvent.Kind<?>[] events,
|
||||||
|
WatchEvent.Modifier... modifiers
|
||||||
|
);
|
||||||
|
|
||||||
////////////////////
|
////////////////////
|
||||||
//
|
//
|
||||||
// Thread management
|
// Thread management
|
||||||
|
@ -674,5 +689,4 @@ public interface EntitlementChecker {
|
||||||
void check$java_lang_Thread$setUncaughtExceptionHandler(Class<?> callerClass, Thread thread, Thread.UncaughtExceptionHandler ueh);
|
void check$java_lang_Thread$setUncaughtExceptionHandler(Class<?> callerClass, Thread thread, Thread.UncaughtExceptionHandler ueh);
|
||||||
|
|
||||||
void check$java_lang_ThreadGroup$setMaxPriority(Class<?> callerClass, ThreadGroup threadGroup, int pri);
|
void check$java_lang_ThreadGroup$setMaxPriority(Class<?> callerClass, ThreadGroup threadGroup, int pri);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the "Elastic License
|
||||||
|
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||||
|
* Public License v 1"; you may not use this file except in compliance with, at
|
||||||
|
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||||
|
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.entitlement.qa.test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.FileSystems;
|
||||||
|
import java.nio.file.LinkOption;
|
||||||
|
import java.nio.file.WatchEvent;
|
||||||
|
|
||||||
|
import static org.elasticsearch.entitlement.qa.test.EntitlementTest.ExpectedAccess.PLUGINS;
|
||||||
|
|
||||||
|
class PathActions {
|
||||||
|
|
||||||
|
@EntitlementTest(expectedAccess = PLUGINS)
|
||||||
|
static void checkToRealPath() throws IOException {
|
||||||
|
FileCheckActions.readFile().toRealPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
@EntitlementTest(expectedAccess = PLUGINS)
|
||||||
|
static void checkToRealPathNoFollow() throws IOException {
|
||||||
|
FileCheckActions.readFile().toRealPath(LinkOption.NOFOLLOW_LINKS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
@EntitlementTest(expectedAccess = PLUGINS)
|
||||||
|
static void checkRegister() throws IOException {
|
||||||
|
try (var watchService = FileSystems.getDefault().newWatchService()) {
|
||||||
|
FileCheckActions.readFile().register(watchService, new WatchEvent.Kind[0]);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
// intentionally no events registered
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
@EntitlementTest(expectedAccess = PLUGINS)
|
||||||
|
static void checkRegisterWithModifiers() throws IOException {
|
||||||
|
try (var watchService = FileSystems.getDefault().newWatchService()) {
|
||||||
|
FileCheckActions.readFile().register(watchService, new WatchEvent.Kind[0], new WatchEvent.Modifier[0]);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
// intentionally no events registered
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -190,6 +190,7 @@ public class RestEntitlementsCheckAction extends BaseRestHandler {
|
||||||
getTestEntries(ManageThreadsActions.class),
|
getTestEntries(ManageThreadsActions.class),
|
||||||
getTestEntries(NativeActions.class),
|
getTestEntries(NativeActions.class),
|
||||||
getTestEntries(NioFileSystemActions.class),
|
getTestEntries(NioFileSystemActions.class),
|
||||||
|
getTestEntries(PathActions.class),
|
||||||
getTestEntries(SpiActions.class),
|
getTestEntries(SpiActions.class),
|
||||||
getTestEntries(SystemActions.class)
|
getTestEntries(SystemActions.class)
|
||||||
)
|
)
|
||||||
|
|
|
@ -46,9 +46,12 @@ import java.nio.file.FileSystems;
|
||||||
import java.nio.file.LinkOption;
|
import java.nio.file.LinkOption;
|
||||||
import java.nio.file.OpenOption;
|
import java.nio.file.OpenOption;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.WatchEvent;
|
||||||
|
import java.nio.file.WatchService;
|
||||||
import java.nio.file.attribute.FileAttribute;
|
import java.nio.file.attribute.FileAttribute;
|
||||||
import java.nio.file.spi.FileSystemProvider;
|
import java.nio.file.spi.FileSystemProvider;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -96,6 +99,7 @@ public class EntitlementInitialization {
|
||||||
Stream.of(
|
Stream.of(
|
||||||
fileSystemProviderChecks(),
|
fileSystemProviderChecks(),
|
||||||
fileStoreChecks(),
|
fileStoreChecks(),
|
||||||
|
pathChecks(),
|
||||||
Stream.of(
|
Stream.of(
|
||||||
INSTRUMENTATION_SERVICE.lookupImplementationMethod(
|
INSTRUMENTATION_SERVICE.lookupImplementationMethod(
|
||||||
SelectorProvider.class,
|
SelectorProvider.class,
|
||||||
|
@ -149,33 +153,49 @@ public class EntitlementInitialization {
|
||||||
new LoadNativeLibrariesEntitlement(),
|
new LoadNativeLibrariesEntitlement(),
|
||||||
new ManageThreadsEntitlement(),
|
new ManageThreadsEntitlement(),
|
||||||
new FilesEntitlement(
|
new FilesEntitlement(
|
||||||
List.of(
|
Stream.concat(
|
||||||
FileData.ofPath(bootstrapArgs.tempDir(), READ_WRITE),
|
Stream.of(
|
||||||
FileData.ofPath(bootstrapArgs.logsDir(), READ_WRITE),
|
FileData.ofPath(bootstrapArgs.tempDir(), READ_WRITE),
|
||||||
// OS release on Linux
|
FileData.ofPath(bootstrapArgs.configDir(), READ),
|
||||||
FileData.ofPath(Path.of("/etc/os-release"), READ),
|
FileData.ofPath(bootstrapArgs.logsDir(), READ_WRITE),
|
||||||
FileData.ofPath(Path.of("/etc/system-release"), READ),
|
// OS release on Linux
|
||||||
FileData.ofPath(Path.of("/usr/lib/os-release"), READ),
|
FileData.ofPath(Path.of("/etc/os-release"), READ),
|
||||||
// read max virtual memory areas
|
FileData.ofPath(Path.of("/etc/system-release"), READ),
|
||||||
FileData.ofPath(Path.of("/proc/sys/vm/max_map_count"), READ),
|
FileData.ofPath(Path.of("/usr/lib/os-release"), READ),
|
||||||
FileData.ofPath(Path.of("/proc/meminfo"), READ),
|
// read max virtual memory areas
|
||||||
// load averages on Linux
|
FileData.ofPath(Path.of("/proc/sys/vm/max_map_count"), READ),
|
||||||
FileData.ofPath(Path.of("/proc/loadavg"), READ),
|
FileData.ofPath(Path.of("/proc/meminfo"), READ),
|
||||||
// control group stats on Linux. cgroup v2 stats are in an unpredicable
|
// load averages on Linux
|
||||||
// location under `/sys/fs/cgroup`, so unfortunately we have to allow
|
FileData.ofPath(Path.of("/proc/loadavg"), READ),
|
||||||
// read access to the entire directory hierarchy.
|
// control group stats on Linux. cgroup v2 stats are in an unpredicable
|
||||||
FileData.ofPath(Path.of("/proc/self/cgroup"), READ),
|
// location under `/sys/fs/cgroup`, so unfortunately we have to allow
|
||||||
FileData.ofPath(Path.of("/sys/fs/cgroup/"), READ),
|
// read access to the entire directory hierarchy.
|
||||||
// // io stats on Linux
|
FileData.ofPath(Path.of("/proc/self/cgroup"), READ),
|
||||||
FileData.ofPath(Path.of("/proc/self/mountinfo"), READ),
|
FileData.ofPath(Path.of("/sys/fs/cgroup/"), READ),
|
||||||
FileData.ofPath(Path.of("/proc/diskstats"), READ)
|
// // io stats on Linux
|
||||||
)
|
FileData.ofPath(Path.of("/proc/self/mountinfo"), READ),
|
||||||
|
FileData.ofPath(Path.of("/proc/diskstats"), READ)
|
||||||
|
),
|
||||||
|
Arrays.stream(bootstrapArgs.dataDirs()).map(d -> FileData.ofPath(d, READ))
|
||||||
|
).toList()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
new Scope("org.apache.httpcomponents.httpclient", List.of(new OutboundNetworkEntitlement())),
|
new Scope("org.apache.httpcomponents.httpclient", List.of(new OutboundNetworkEntitlement())),
|
||||||
new Scope("io.netty.transport", List.of(new InboundNetworkEntitlement(), new OutboundNetworkEntitlement())),
|
new Scope("io.netty.transport", List.of(new InboundNetworkEntitlement(), new OutboundNetworkEntitlement())),
|
||||||
new Scope("org.apache.lucene.core", List.of(new LoadNativeLibrariesEntitlement(), new ManageThreadsEntitlement())),
|
new Scope(
|
||||||
|
"org.apache.lucene.core",
|
||||||
|
List.of(
|
||||||
|
new LoadNativeLibrariesEntitlement(),
|
||||||
|
new ManageThreadsEntitlement(),
|
||||||
|
new FilesEntitlement(
|
||||||
|
Stream.concat(
|
||||||
|
Stream.of(FileData.ofPath(bootstrapArgs.configDir(), READ)),
|
||||||
|
Arrays.stream(bootstrapArgs.dataDirs()).map(d -> FileData.ofPath(d, READ_WRITE))
|
||||||
|
).toList()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
new Scope("org.apache.logging.log4j.core", List.of(new ManageThreadsEntitlement())),
|
new Scope("org.apache.logging.log4j.core", List.of(new ManageThreadsEntitlement())),
|
||||||
new Scope(
|
new Scope(
|
||||||
"org.elasticsearch.nativeaccess",
|
"org.elasticsearch.nativeaccess",
|
||||||
|
@ -289,6 +309,33 @@ public class EntitlementInitialization {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Stream<InstrumentationService.InstrumentationInfo> pathChecks() {
|
||||||
|
var pathClasses = StreamSupport.stream(FileSystems.getDefault().getRootDirectories().spliterator(), false)
|
||||||
|
.map(Path::getClass)
|
||||||
|
.distinct();
|
||||||
|
return pathClasses.flatMap(pathClass -> {
|
||||||
|
InstrumentationInfoFactory instrumentation = (String methodName, Class<?>... parameterTypes) -> INSTRUMENTATION_SERVICE
|
||||||
|
.lookupImplementationMethod(
|
||||||
|
Path.class,
|
||||||
|
methodName,
|
||||||
|
pathClass,
|
||||||
|
EntitlementChecker.class,
|
||||||
|
"checkPath" + Character.toUpperCase(methodName.charAt(0)) + methodName.substring(1),
|
||||||
|
parameterTypes
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return Stream.of(
|
||||||
|
instrumentation.of("toRealPath", LinkOption[].class),
|
||||||
|
instrumentation.of("register", WatchService.class, WatchEvent.Kind[].class),
|
||||||
|
instrumentation.of("register", WatchService.class, WatchEvent.Kind[].class, WatchEvent.Modifier[].class)
|
||||||
|
);
|
||||||
|
} catch (NoSuchMethodException | ClassNotFoundException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the "most recent" checker class compatible with the current runtime Java version.
|
* Returns the "most recent" checker class compatible with the current runtime Java version.
|
||||||
* For checkers, we have (optionally) version specific classes, each with a prefix (e.g. Java23).
|
* For checkers, we have (optionally) version specific classes, each with a prefix (e.g. Java23).
|
||||||
|
|
|
@ -14,6 +14,7 @@ import org.elasticsearch.entitlement.bridge.EntitlementChecker;
|
||||||
import org.elasticsearch.entitlement.runtime.policy.PolicyManager;
|
import org.elasticsearch.entitlement.runtime.policy.PolicyManager;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
@ -60,10 +61,13 @@ import java.nio.file.AccessMode;
|
||||||
import java.nio.file.CopyOption;
|
import java.nio.file.CopyOption;
|
||||||
import java.nio.file.DirectoryStream;
|
import java.nio.file.DirectoryStream;
|
||||||
import java.nio.file.FileStore;
|
import java.nio.file.FileStore;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.nio.file.LinkOption;
|
import java.nio.file.LinkOption;
|
||||||
import java.nio.file.OpenOption;
|
import java.nio.file.OpenOption;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.nio.file.WatchEvent;
|
||||||
|
import java.nio.file.WatchService;
|
||||||
import java.nio.file.attribute.FileAttribute;
|
import java.nio.file.attribute.FileAttribute;
|
||||||
import java.nio.file.attribute.UserPrincipal;
|
import java.nio.file.attribute.UserPrincipal;
|
||||||
import java.nio.file.spi.FileSystemProvider;
|
import java.nio.file.spi.FileSystemProvider;
|
||||||
|
@ -1369,4 +1373,38 @@ public class ElasticsearchEntitlementChecker implements EntitlementChecker {
|
||||||
public void checkType(Class<?> callerClass, FileStore that) {
|
public void checkType(Class<?> callerClass, FileStore that) {
|
||||||
policyManager.checkReadStoreAttributes(callerClass);
|
policyManager.checkReadStoreAttributes(callerClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkPathToRealPath(Class<?> callerClass, Path that, LinkOption... options) {
|
||||||
|
boolean followLinks = true;
|
||||||
|
for (LinkOption option : options) {
|
||||||
|
if (option == LinkOption.NOFOLLOW_LINKS) {
|
||||||
|
followLinks = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (followLinks) {
|
||||||
|
try {
|
||||||
|
policyManager.checkFileRead(callerClass, Files.readSymbolicLink(that));
|
||||||
|
} catch (IOException | UnsupportedOperationException e) {
|
||||||
|
// that is not a link, or unrelated IOException or unsupported
|
||||||
|
}
|
||||||
|
}
|
||||||
|
policyManager.checkFileRead(callerClass, that);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkPathRegister(Class<?> callerClass, Path that, WatchService watcher, WatchEvent.Kind<?>... events) {
|
||||||
|
policyManager.checkFileRead(callerClass, that);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkPathRegister(
|
||||||
|
Class<?> callerClass,
|
||||||
|
Path that,
|
||||||
|
WatchService watcher,
|
||||||
|
WatchEvent.Kind<?>[] events,
|
||||||
|
WatchEvent.Modifier... modifiers
|
||||||
|
) {
|
||||||
|
policyManager.checkFileRead(callerClass, that);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,11 @@ import org.apache.tika.parser.Parser;
|
||||||
import org.apache.tika.parser.ParserDecorator;
|
import org.apache.tika.parser.ParserDecorator;
|
||||||
import org.elasticsearch.SpecialPermission;
|
import org.elasticsearch.SpecialPermission;
|
||||||
import org.elasticsearch.bootstrap.FilePermissionUtils;
|
import org.elasticsearch.bootstrap.FilePermissionUtils;
|
||||||
|
import org.elasticsearch.core.Booleans;
|
||||||
import org.elasticsearch.core.PathUtils;
|
import org.elasticsearch.core.PathUtils;
|
||||||
import org.elasticsearch.core.SuppressForbidden;
|
import org.elasticsearch.core.SuppressForbidden;
|
||||||
import org.elasticsearch.jdk.JarHell;
|
import org.elasticsearch.jdk.JarHell;
|
||||||
|
import org.elasticsearch.jdk.RuntimeVersionFeature;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -122,15 +124,22 @@ final class TikaImpl {
|
||||||
|
|
||||||
// apply additional containment for parsers, this is intersected with the current permissions
|
// apply additional containment for parsers, this is intersected with the current permissions
|
||||||
// its hairy, but worth it so we don't have some XML flaw reading random crap from the FS
|
// its hairy, but worth it so we don't have some XML flaw reading random crap from the FS
|
||||||
private static final AccessControlContext RESTRICTED_CONTEXT = new AccessControlContext(
|
private static final AccessControlContext RESTRICTED_CONTEXT = isUsingSecurityManager()
|
||||||
new ProtectionDomain[] { new ProtectionDomain(null, getRestrictedPermissions()) }
|
? new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, getRestrictedPermissions()) })
|
||||||
);
|
: null;
|
||||||
|
|
||||||
|
private static boolean isUsingSecurityManager() {
|
||||||
|
boolean entitlementsEnabled = Booleans.parseBoolean(System.getProperty("es.entitlements.enabled"), false)
|
||||||
|
|| RuntimeVersionFeature.isSecurityManagerAvailable() == false;
|
||||||
|
return entitlementsEnabled == false;
|
||||||
|
}
|
||||||
|
|
||||||
// compute some minimal permissions for parsers. they only get r/w access to the java temp directory,
|
// compute some minimal permissions for parsers. they only get r/w access to the java temp directory,
|
||||||
// the ability to load some resources from JARs, and read sysprops
|
// the ability to load some resources from JARs, and read sysprops
|
||||||
@SuppressForbidden(reason = "adds access to tmp directory")
|
@SuppressForbidden(reason = "adds access to tmp directory")
|
||||||
static PermissionCollection getRestrictedPermissions() {
|
static PermissionCollection getRestrictedPermissions() {
|
||||||
Permissions perms = new Permissions();
|
Permissions perms = new Permissions();
|
||||||
|
|
||||||
// property/env access needed for parsing
|
// property/env access needed for parsing
|
||||||
perms.add(new PropertyPermission("*", "read"));
|
perms.add(new PropertyPermission("*", "read"));
|
||||||
perms.add(new RuntimePermission("getenv.TIKA_CONFIG"));
|
perms.add(new RuntimePermission("getenv.TIKA_CONFIG"));
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue