From e50bde32bd0c6861371575b233108d3d39209655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorenzo=20Dematt=C3=A9?= Date: Thu, 20 Feb 2025 09:33:38 +0100 Subject: [PATCH] [Entitlements] Instrumentation of NIO Files and Channels (#122816 and #122591) (#122986) * [Entitlements] Add check functions for NIO Files (#122591) * [Entitlements] Instrumentation of NIO file channels (#122816) * [CI] Auto commit changes from spotless --------- Co-authored-by: elasticsearchmachine --- .../server/cli/SystemJvmOptions.java | 2 +- .../bridge/src/main/java/module-info.java | 1 + .../bridge/EntitlementChecker.java | 208 ++++++++ .../src/main/java/module-info.java | 1 + .../qa/test/DummyImplementations.java | 172 ++++++ .../entitlement/qa/test/FileCheckActions.java | 18 - .../qa/test/NioChannelsActions.java | 87 ++++ .../entitlement/qa/test/NioFilesActions.java | 475 +++++++++++++++++ .../qa/test/RestEntitlementsCheckAction.java | 2 + .../entitlement/qa/EntitlementsTestRule.java | 2 +- .../src/main/java/module-info.java | 1 + .../bootstrap/EntitlementBootstrap.java | 7 + .../EntitlementInitialization.java | 89 ++-- .../api/ElasticsearchEntitlementChecker.java | 493 ++++++++++++++++++ .../runtime/policy/PolicyManager.java | 16 + .../plugin-metadata/entitlement-policy.yaml | 5 + .../plugin-metadata/entitlement-policy.yaml | 11 +- .../plugin-metadata/entitlement-policy.yaml | 5 + .../plugin-metadata/entitlement-policy.yaml | 4 + .../plugin-metadata/entitlement-policy.yaml | 7 + .../plugin-metadata/entitlement-policy.yaml | 2 + .../plugin-metadata/entitlement-policy.yaml | 5 + .../plugin-metadata/entitlement-policy.yaml | 4 + .../plugin-metadata/entitlement-policy.yaml | 5 + .../bootstrap/Elasticsearch.java | 1 + .../plugin-metadata/entitlement-policy.yaml | 6 +- .../plugin-metadata/entitlement-policy.yaml | 10 + .../plugin-metadata/entitlement-policy.yaml | 4 + .../plugin-metadata/entitlement-policy.yaml | 7 + .../plugin-metadata/entitlement-policy.yaml | 8 + .../plugin-metadata/entitlement-policy.yaml | 6 + 31 files changed, 1606 insertions(+), 58 deletions(-) create mode 100644 libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/NioChannelsActions.java create mode 100644 libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/NioFilesActions.java create mode 100644 modules/analysis-common/src/main/plugin-metadata/entitlement-policy.yaml create mode 100644 modules/ingest-user-agent/src/main/plugin-metadata/entitlement-policy.yaml create mode 100644 plugins/analysis-icu/src/main/plugin-metadata/entitlement-policy.yaml create mode 100644 plugins/store-smb/src/main/plugin-metadata/entitlement-policy.yaml create mode 100644 x-pack/plugin/searchable-snapshots/src/main/plugin-metadata/entitlement-policy.yaml diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/SystemJvmOptions.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/SystemJvmOptions.java index 21294f1c5f01b..f8fd8e1885e56 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/SystemJvmOptions.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/SystemJvmOptions.java @@ -189,7 +189,7 @@ private static Stream maybeAttachEntitlementAgent(boolean useEntitlement } // We instrument classes in these modules to call the bridge. Because the bridge gets patched // into java.base, we must export the bridge from java.base to these modules, as a comma-separated list - String modulesContainingEntitlementInstrumentation = "java.logging,java.net.http,java.naming"; + String modulesContainingEntitlementInstrumentation = "java.logging,java.net.http,java.naming,jdk.net"; return Stream.of( "-Des.entitlements.enabled=true", "-XX:+EnableDynamicAgentLoading", diff --git a/libs/entitlement/bridge/src/main/java/module-info.java b/libs/entitlement/bridge/src/main/java/module-info.java index b9055ec5fbf67..518a0a1ef29ec 100644 --- a/libs/entitlement/bridge/src/main/java/module-info.java +++ b/libs/entitlement/bridge/src/main/java/module-info.java @@ -11,6 +11,7 @@ // At build and run time, the bridge is patched into the java.base module. module org.elasticsearch.entitlement.bridge { requires java.net.http; + requires jdk.net; exports org.elasticsearch.entitlement.bridge; } diff --git a/libs/entitlement/bridge/src/main/java/org/elasticsearch/entitlement/bridge/EntitlementChecker.java b/libs/entitlement/bridge/src/main/java/org/elasticsearch/entitlement/bridge/EntitlementChecker.java index 9ec0e399129f0..e4bb493236dc7 100644 --- a/libs/entitlement/bridge/src/main/java/org/elasticsearch/entitlement/bridge/EntitlementChecker.java +++ b/libs/entitlement/bridge/src/main/java/org/elasticsearch/entitlement/bridge/EntitlementChecker.java @@ -9,11 +9,14 @@ package org.elasticsearch.entitlement.bridge; +import jdk.nio.Channels; + import java.io.File; import java.io.FileDescriptor; import java.io.FileFilter; import java.io.FilenameFilter; import java.io.InputStream; +import java.io.OutputStream; import java.io.PrintStream; import java.io.PrintWriter; import java.net.ContentHandlerFactory; @@ -51,12 +54,18 @@ import java.nio.file.CopyOption; import java.nio.file.DirectoryStream; import java.nio.file.FileStore; +import java.nio.file.FileVisitOption; +import java.nio.file.FileVisitor; import java.nio.file.LinkOption; import java.nio.file.OpenOption; import java.nio.file.Path; import java.nio.file.WatchEvent; import java.nio.file.WatchService; +import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.FileAttributeView; +import java.nio.file.attribute.FileTime; +import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.UserPrincipal; import java.nio.file.spi.FileSystemProvider; import java.security.KeyStore; @@ -70,6 +79,7 @@ import java.util.TimeZone; import java.util.concurrent.ExecutorService; import java.util.concurrent.ForkJoinPool; +import java.util.function.BiPredicate; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; @@ -606,12 +616,210 @@ public interface EntitlementChecker { void check$java_util_zip_ZipFile$(Class callerClass, File file, int mode, Charset charset); // nio + // channels + void check$java_nio_channels_FileChannel$(Class callerClass); + + void check$java_nio_channels_FileChannel$$open( + Class callerClass, + Path path, + Set options, + FileAttribute... attrs + ); + + void check$java_nio_channels_FileChannel$$open(Class callerClass, Path path, OpenOption... options); + + void check$java_nio_channels_AsynchronousFileChannel$(Class callerClass); + + void check$java_nio_channels_AsynchronousFileChannel$$open( + Class callerClass, + Path path, + Set options, + ExecutorService executor, + FileAttribute... attrs + ); + + void check$java_nio_channels_AsynchronousFileChannel$$open(Class callerClass, Path path, OpenOption... options); + + void check$jdk_nio_Channels$$readWriteSelectableChannel( + Class callerClass, + FileDescriptor fd, + Channels.SelectableChannelCloser closer + ); + + // files void check$java_nio_file_Files$$getOwner(Class callerClass, Path path, LinkOption... options); void check$java_nio_file_Files$$probeContentType(Class callerClass, Path path); void check$java_nio_file_Files$$setOwner(Class callerClass, Path path, UserPrincipal principal); + void check$java_nio_file_Files$$newInputStream(Class callerClass, Path path, OpenOption... options); + + void check$java_nio_file_Files$$newOutputStream(Class callerClass, Path path, OpenOption... options); + + void check$java_nio_file_Files$$newByteChannel( + Class callerClass, + Path path, + Set options, + FileAttribute... attrs + ); + + void check$java_nio_file_Files$$newByteChannel(Class callerClass, Path path, OpenOption... options); + + void check$java_nio_file_Files$$newDirectoryStream(Class callerClass, Path dir); + + void check$java_nio_file_Files$$newDirectoryStream(Class callerClass, Path dir, String glob); + + void check$java_nio_file_Files$$newDirectoryStream(Class callerClass, Path dir, DirectoryStream.Filter filter); + + void check$java_nio_file_Files$$createFile(Class callerClass, Path path, FileAttribute... attrs); + + void check$java_nio_file_Files$$createDirectory(Class callerClass, Path dir, FileAttribute... attrs); + + void check$java_nio_file_Files$$createDirectories(Class callerClass, Path dir, FileAttribute... attrs); + + void check$java_nio_file_Files$$createTempFile(Class callerClass, Path dir, String prefix, String suffix, FileAttribute... attrs); + + void check$java_nio_file_Files$$createTempFile(Class callerClass, String prefix, String suffix, FileAttribute... attrs); + + void check$java_nio_file_Files$$createTempDirectory(Class callerClass, Path dir, String prefix, FileAttribute... attrs); + + void check$java_nio_file_Files$$createTempDirectory(Class callerClass, String prefix, FileAttribute... attrs); + + void check$java_nio_file_Files$$createSymbolicLink(Class callerClass, Path link, Path target, FileAttribute... attrs); + + void check$java_nio_file_Files$$createLink(Class callerClass, Path link, Path existing); + + void check$java_nio_file_Files$$delete(Class callerClass, Path path); + + void check$java_nio_file_Files$$deleteIfExists(Class callerClass, Path path); + + void check$java_nio_file_Files$$copy(Class callerClass, Path source, Path target, CopyOption... options); + + void check$java_nio_file_Files$$move(Class callerClass, Path source, Path target, CopyOption... options); + + void check$java_nio_file_Files$$readSymbolicLink(Class callerClass, Path link); + + void check$java_nio_file_Files$$getFileStore(Class callerClass, Path path); + + void check$java_nio_file_Files$$isSameFile(Class callerClass, Path path, Path path2); + + void check$java_nio_file_Files$$mismatch(Class callerClass, Path path, Path path2); + + void check$java_nio_file_Files$$isHidden(Class callerClass, Path path); + + void check$java_nio_file_Files$$getFileAttributeView( + Class callerClass, + Path path, + Class type, + LinkOption... options + ); + + void check$java_nio_file_Files$$readAttributes( + Class callerClass, + Path path, + Class type, + LinkOption... options + ); + + void check$java_nio_file_Files$$setAttribute(Class callerClass, Path path, String attribute, Object value, LinkOption... options); + + void check$java_nio_file_Files$$getAttribute(Class callerClass, Path path, String attribute, LinkOption... options); + + void check$java_nio_file_Files$$readAttributes(Class callerClass, Path path, String attributes, LinkOption... options); + + void check$java_nio_file_Files$$getPosixFilePermissions(Class callerClass, Path path, LinkOption... options); + + void check$java_nio_file_Files$$setPosixFilePermissions(Class callerClass, Path path, Set perms); + + void check$java_nio_file_Files$$isSymbolicLink(Class callerClass, Path path); + + void check$java_nio_file_Files$$isDirectory(Class callerClass, Path path, LinkOption... options); + + void check$java_nio_file_Files$$isRegularFile(Class callerClass, Path path, LinkOption... options); + + void check$java_nio_file_Files$$getLastModifiedTime(Class callerClass, Path path, LinkOption... options); + + void check$java_nio_file_Files$$setLastModifiedTime(Class callerClass, Path path, FileTime time); + + void check$java_nio_file_Files$$size(Class callerClass, Path path); + + void check$java_nio_file_Files$$exists(Class callerClass, Path path, LinkOption... options); + + void check$java_nio_file_Files$$notExists(Class callerClass, Path path, LinkOption... options); + + void check$java_nio_file_Files$$isReadable(Class callerClass, Path path); + + void check$java_nio_file_Files$$isWritable(Class callerClass, Path path); + + void check$java_nio_file_Files$$isExecutable(Class callerClass, Path path); + + void check$java_nio_file_Files$$walkFileTree( + Class callerClass, + Path start, + Set options, + int maxDepth, + FileVisitor visitor + ); + + void check$java_nio_file_Files$$walkFileTree(Class callerClass, Path start, FileVisitor visitor); + + void check$java_nio_file_Files$$newBufferedReader(Class callerClass, Path path, Charset cs); + + void check$java_nio_file_Files$$newBufferedReader(Class callerClass, Path path); + + void check$java_nio_file_Files$$newBufferedWriter(Class callerClass, Path path, Charset cs, OpenOption... options); + + void check$java_nio_file_Files$$newBufferedWriter(Class callerClass, Path path, OpenOption... options); + + void check$java_nio_file_Files$$copy(Class callerClass, InputStream in, Path target, CopyOption... options); + + void check$java_nio_file_Files$$copy(Class callerClass, Path source, OutputStream out); + + void check$java_nio_file_Files$$readAllBytes(Class callerClass, Path path); + + void check$java_nio_file_Files$$readString(Class callerClass, Path path); + + void check$java_nio_file_Files$$readString(Class callerClass, Path path, Charset cs); + + void check$java_nio_file_Files$$readAllLines(Class callerClass, Path path, Charset cs); + + void check$java_nio_file_Files$$readAllLines(Class callerClass, Path path); + + void check$java_nio_file_Files$$write(Class callerClass, Path path, byte[] bytes, OpenOption... options); + + void check$java_nio_file_Files$$write( + Class callerClass, + Path path, + Iterable lines, + Charset cs, + OpenOption... options + ); + + void check$java_nio_file_Files$$write(Class callerClass, Path path, Iterable lines, OpenOption... options); + + void check$java_nio_file_Files$$writeString(Class callerClass, Path path, CharSequence csq, OpenOption... options); + + void check$java_nio_file_Files$$writeString(Class callerClass, Path path, CharSequence csq, Charset cs, OpenOption... options); + + void check$java_nio_file_Files$$list(Class callerClass, Path dir); + + void check$java_nio_file_Files$$walk(Class callerClass, Path start, int maxDepth, FileVisitOption... options); + + void check$java_nio_file_Files$$walk(Class callerClass, Path start, FileVisitOption... options); + + void check$java_nio_file_Files$$find( + Class callerClass, + Path start, + int maxDepth, + BiPredicate matcher, + FileVisitOption... options + ); + + void check$java_nio_file_Files$$lines(Class callerClass, Path path, Charset cs); + + void check$java_nio_file_Files$$lines(Class callerClass, Path path); + // file system providers void check$java_nio_file_spi_FileSystemProvider$(Class callerClass); diff --git a/libs/entitlement/qa/entitlement-test-plugin/src/main/java/module-info.java b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/module-info.java index bb4c6fd759426..ee2ae33d34890 100644 --- a/libs/entitlement/qa/entitlement-test-plugin/src/main/java/module-info.java +++ b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/module-info.java @@ -16,4 +16,5 @@ // Modules we'll attempt to use in order to exercise entitlements requires java.logging; requires java.net.http; + requires jdk.net; } diff --git a/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/DummyImplementations.java b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/DummyImplementations.java index ca03014634076..929ec4ce731d3 100644 --- a/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/DummyImplementations.java +++ b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/DummyImplementations.java @@ -9,6 +9,10 @@ package org.elasticsearch.entitlement.qa.test; +import jdk.nio.Channels; + +import org.elasticsearch.core.SuppressForbidden; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -24,14 +28,23 @@ import java.net.SocketException; import java.net.SocketImpl; import java.net.URI; +import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; import java.nio.channels.AsynchronousChannelGroup; +import java.nio.channels.AsynchronousFileChannel; import java.nio.channels.AsynchronousServerSocketChannel; import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.CompletionHandler; import java.nio.channels.DatagramChannel; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; import java.nio.channels.Pipe; +import java.nio.channels.ReadableByteChannel; import java.nio.channels.SeekableByteChannel; +import java.nio.channels.SelectableChannel; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; +import java.nio.channels.WritableByteChannel; import java.nio.channels.spi.AbstractSelector; import java.nio.channels.spi.AsynchronousChannelProvider; import java.nio.channels.spi.SelectorProvider; @@ -67,6 +80,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import java.util.spi.CalendarDataProvider; import java.util.spi.CalendarNameProvider; @@ -676,4 +690,162 @@ public void setAttribute(Path path, String attribute, Object value, LinkOption.. } } + + static class DummyFileChannel extends FileChannel { + @Override + protected void implCloseChannel() throws IOException { + + } + + @Override + public int read(ByteBuffer dst) throws IOException { + return 0; + } + + @Override + public long read(ByteBuffer[] dsts, int offset, int length) throws IOException { + return 0; + } + + @Override + public int write(ByteBuffer src) throws IOException { + return 0; + } + + @Override + public long write(ByteBuffer[] srcs, int offset, int length) throws IOException { + return 0; + } + + @Override + public long position() throws IOException { + return 0; + } + + @Override + public FileChannel position(long newPosition) throws IOException { + return null; + } + + @Override + public long size() throws IOException { + return 0; + } + + @Override + public FileChannel truncate(long size) throws IOException { + return null; + } + + @Override + public void force(boolean metaData) throws IOException { + + } + + @Override + public long transferTo(long position, long count, WritableByteChannel target) throws IOException { + return 0; + } + + @Override + public long transferFrom(ReadableByteChannel src, long position, long count) throws IOException { + return 0; + } + + @Override + public int read(ByteBuffer dst, long position) throws IOException { + return 0; + } + + @Override + public int write(ByteBuffer src, long position) throws IOException { + return 0; + } + + @Override + public MappedByteBuffer map(MapMode mode, long position, long size) throws IOException { + return null; + } + + @Override + public FileLock lock(long position, long size, boolean shared) throws IOException { + return null; + } + + @Override + public FileLock tryLock(long position, long size, boolean shared) throws IOException { + return null; + } + } + + static class DummyAsynchronousFileChannel extends AsynchronousFileChannel { + @Override + public boolean isOpen() { + return false; + } + + @Override + public void close() throws IOException { + + } + + @Override + public long size() throws IOException { + return 0; + } + + @Override + public AsynchronousFileChannel truncate(long size) throws IOException { + return null; + } + + @Override + public void force(boolean metaData) throws IOException { + + } + + @Override + public void lock(long position, long size, boolean shared, A attachment, CompletionHandler handler) { + + } + + @Override + public Future lock(long position, long size, boolean shared) { + return null; + } + + @Override + public FileLock tryLock(long position, long size, boolean shared) throws IOException { + return null; + } + + @Override + public void read(ByteBuffer dst, long position, A attachment, CompletionHandler handler) { + + } + + @Override + public Future read(ByteBuffer dst, long position) { + return null; + } + + @Override + public void write(ByteBuffer src, long position, A attachment, CompletionHandler handler) { + + } + + @Override + public Future write(ByteBuffer src, long position) { + return null; + } + } + + @SuppressForbidden(reason = "specifically testing readWriteSelectableChannel") + static class DummySelectableChannelCloser implements Channels.SelectableChannelCloser { + @Override + public void implCloseChannel(SelectableChannel sc) throws IOException {} + + @Override + public void implReleaseChannel(SelectableChannel sc) throws IOException {} + } } diff --git a/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/FileCheckActions.java b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/FileCheckActions.java index 05fbc716e5723..aa0fdc33a36ec 100644 --- a/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/FileCheckActions.java +++ b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/FileCheckActions.java @@ -23,10 +23,8 @@ import java.io.IOException; import java.io.RandomAccessFile; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.nio.file.attribute.UserPrincipal; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.util.Scanner; @@ -343,22 +341,6 @@ static void createRandomAccessFileReadWrite() throws IOException { new RandomAccessFile(readWriteFile().toFile(), "rw").close(); } - @EntitlementTest(expectedAccess = PLUGINS) - static void filesGetOwner() throws IOException { - Files.getOwner(readFile()); - } - - @EntitlementTest(expectedAccess = PLUGINS) - static void filesProbeContentType() throws IOException { - Files.probeContentType(readFile()); - } - - @EntitlementTest(expectedAccess = PLUGINS) - static void filesSetOwner() throws IOException { - UserPrincipal owner = EntitledActions.getFileOwner(readWriteFile()); - Files.setOwner(readWriteFile(), owner); // set to existing owner, just trying to execute the method - } - @EntitlementTest(expectedAccess = PLUGINS) static void keystoreGetInstance_FileCharArray() throws IOException { try { diff --git a/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/NioChannelsActions.java b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/NioChannelsActions.java new file mode 100644 index 0000000000000..777f0fbf67a9f --- /dev/null +++ b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/NioChannelsActions.java @@ -0,0 +1,87 @@ +/* + * 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 org.elasticsearch.common.util.concurrent.EsExecutors; +import org.elasticsearch.core.SuppressForbidden; +import org.elasticsearch.entitlement.qa.entitled.EntitledActions; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.nio.channels.AsynchronousFileChannel; +import java.nio.channels.FileChannel; +import java.nio.file.StandardOpenOption; +import java.util.Set; + +import static org.elasticsearch.entitlement.qa.test.EntitlementTest.ExpectedAccess.ALWAYS_DENIED; +import static org.elasticsearch.entitlement.qa.test.EntitlementTest.ExpectedAccess.PLUGINS; + +class NioChannelsActions { + + @EntitlementTest(expectedAccess = ALWAYS_DENIED) + static void createFileChannel() throws IOException { + new DummyImplementations.DummyFileChannel().close(); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void fileChannelOpenForWrite() throws IOException { + FileChannel.open(FileCheckActions.readWriteFile(), StandardOpenOption.WRITE).close(); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void fileChannelOpenForRead() throws IOException { + FileChannel.open(FileCheckActions.readFile()).close(); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void fileChannelOpenForWriteWithOptions() throws IOException { + FileChannel.open(FileCheckActions.readWriteFile(), Set.of(StandardOpenOption.WRITE)).close(); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void fileChannelOpenForReadWithOptions() throws IOException { + FileChannel.open(FileCheckActions.readFile(), Set.of(StandardOpenOption.READ)).close(); + } + + @EntitlementTest(expectedAccess = ALWAYS_DENIED) + static void createAsynchronousFileChannel() throws IOException { + new DummyImplementations.DummyAsynchronousFileChannel().close(); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void asynchronousFileChannelOpenForWrite() throws IOException { + var file = EntitledActions.createTempFileForWrite(); + AsynchronousFileChannel.open(file, StandardOpenOption.WRITE).close(); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void asynchronousFileChannelOpenForRead() throws IOException { + var file = EntitledActions.createTempFileForRead(); + AsynchronousFileChannel.open(file).close(); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void asynchronousFileChannelOpenForWriteWithOptions() throws IOException { + var file = EntitledActions.createTempFileForWrite(); + AsynchronousFileChannel.open(file, Set.of(StandardOpenOption.WRITE), EsExecutors.DIRECT_EXECUTOR_SERVICE).close(); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void asynchronousFileChannelOpenForReadWithOptions() throws IOException { + var file = EntitledActions.createTempFileForRead(); + AsynchronousFileChannel.open(file, Set.of(StandardOpenOption.READ), EsExecutors.DIRECT_EXECUTOR_SERVICE).close(); + } + + @SuppressForbidden(reason = "specifically testing jdk.nio.Channels") + @EntitlementTest(expectedAccess = ALWAYS_DENIED) + static void channelsReadWriteSelectableChannel() throws IOException { + jdk.nio.Channels.readWriteSelectableChannel(new FileDescriptor(), new DummyImplementations.DummySelectableChannelCloser()).close(); + } +} diff --git a/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/NioFilesActions.java b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/NioFilesActions.java new file mode 100644 index 0000000000000..d4e8938b4b23e --- /dev/null +++ b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/NioFilesActions.java @@ -0,0 +1,475 @@ +/* + * 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 org.elasticsearch.core.SuppressForbidden; +import org.elasticsearch.entitlement.qa.entitled.EntitledActions; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystemException; +import java.nio.file.FileVisitOption; +import java.nio.file.FileVisitResult; +import java.nio.file.FileVisitor; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileOwnerAttributeView; +import java.nio.file.attribute.FileTime; +import java.nio.file.attribute.UserPrincipal; +import java.time.Instant; +import java.util.List; +import java.util.Set; + +import static org.elasticsearch.entitlement.qa.test.EntitlementTest.ExpectedAccess.ALWAYS_DENIED; +import static org.elasticsearch.entitlement.qa.test.EntitlementTest.ExpectedAccess.PLUGINS; +import static org.elasticsearch.entitlement.qa.test.FileCheckActions.readDir; +import static org.elasticsearch.entitlement.qa.test.FileCheckActions.readFile; +import static org.elasticsearch.entitlement.qa.test.FileCheckActions.readWriteDir; +import static org.elasticsearch.entitlement.qa.test.FileCheckActions.readWriteFile; + +class NioFilesActions { + + @EntitlementTest(expectedAccess = PLUGINS) + static void filesGetOwner() throws IOException { + Files.getOwner(readFile()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void filesProbeContentType() throws IOException { + Files.probeContentType(readFile()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void filesSetOwner() throws IOException { + UserPrincipal owner = EntitledActions.getFileOwner(readWriteFile()); + Files.setOwner(readWriteFile(), owner); // set to existing owner, just trying to execute the method + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesNewInputStream() throws IOException { + Files.newInputStream(readFile()).close(); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesNewOutputStream() throws IOException { + Files.newOutputStream(readWriteFile()).close(); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesNewByteChannelRead() throws IOException { + Files.newByteChannel(readFile(), Set.of(StandardOpenOption.READ)).close(); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesNewByteChannelWrite() throws IOException { + Files.newByteChannel(readWriteFile(), Set.of(StandardOpenOption.WRITE)).close(); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesNewByteChannelReadVarargs() throws IOException { + Files.newByteChannel(readFile(), StandardOpenOption.READ).close(); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesNewByteChannelWriteVarargs() throws IOException { + Files.newByteChannel(readWriteFile(), StandardOpenOption.WRITE).close(); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesNewDirectoryStream() throws IOException { + Files.newDirectoryStream(FileCheckActions.readDir()).close(); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesNewDirectoryStreamGlob() throws IOException { + Files.newDirectoryStream(FileCheckActions.readDir(), "*").close(); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesNewDirectoryStreamFilter() throws IOException { + Files.newDirectoryStream(FileCheckActions.readDir(), entry -> false).close(); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesCreateFile() throws IOException { + Files.createFile(readWriteDir().resolve("file.txt")); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesCreateDirectory() throws IOException { + var directory = EntitledActions.createTempDirectoryForWrite(); + Files.createDirectory(directory.resolve("subdir")); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesCreateDirectories() throws IOException { + var directory = EntitledActions.createTempDirectoryForWrite(); + Files.createDirectories(directory.resolve("subdir").resolve("subsubdir")); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesCreateTempFileInDir() throws IOException { + Files.createTempFile(readWriteDir(), "prefix", "suffix"); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesCreateTempDirectoryInDir() throws IOException { + Files.createTempDirectory(readWriteDir(), "prefix"); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesCreateSymbolicLink() throws IOException { + var directory = EntitledActions.createTempDirectoryForWrite(); + try { + Files.createSymbolicLink(directory.resolve("link"), readFile()); + } catch (UnsupportedOperationException | FileSystemException e) { + // OK not to implement symbolic link in the filesystem + } + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesCreateLink() throws IOException { + var directory = EntitledActions.createTempDirectoryForWrite(); + try { + Files.createLink(directory.resolve("link"), readFile()); + } catch (UnsupportedOperationException | FileSystemException e) { + // OK not to implement symbolic link in the filesystem + } + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesDelete() throws IOException { + var file = EntitledActions.createTempFileForWrite(); + Files.delete(file); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesDeleteIfExists() throws IOException { + var file = EntitledActions.createTempFileForWrite(); + Files.deleteIfExists(file); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesReadSymbolicLink() throws IOException { + var link = EntitledActions.createTempSymbolicLink(); + Files.readSymbolicLink(link); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesCopy() throws IOException { + var directory = EntitledActions.createTempDirectoryForWrite(); + Files.copy(readFile(), directory.resolve("copied")); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesMove() throws IOException { + var directory = EntitledActions.createTempDirectoryForWrite(); + var file = EntitledActions.createTempFileForWrite(); + Files.move(file, directory.resolve("moved")); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesIsSameFile() throws IOException { + Files.isSameFile(readWriteFile(), readFile()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesMismatch() throws IOException { + Files.mismatch(readWriteFile(), readFile()); + } + + @SuppressForbidden(reason = "testing entitlements on this API specifically") + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesIsHidden() throws IOException { + Files.isHidden(readFile()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesGetFileStore() throws IOException { + var file = EntitledActions.createTempFileForRead(); + Files.getFileStore(file); + } + + @EntitlementTest(expectedAccess = ALWAYS_DENIED) + static void checkFilesGetFileAttributeView() { + Files.getFileAttributeView(readFile(), FileOwnerAttributeView.class); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesReadAttributesWithClass() throws IOException { + Files.readAttributes(readFile(), BasicFileAttributes.class); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesReadAttributesWithString() throws IOException { + Files.readAttributes(readFile(), "*"); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesGetAttribute() throws IOException { + try { + Files.getAttribute(readFile(), "dos:hidden"); + } catch (UnsupportedOperationException | IllegalArgumentException | FileSystemException e) { + // OK if the file does not have/does not support the attribute + } + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesSetAttribute() throws IOException { + var file = EntitledActions.createTempFileForWrite(); + try { + Files.setAttribute(file, "dos:hidden", true); + } catch (UnsupportedOperationException | IllegalArgumentException | FileSystemException e) { + // OK if the file does not have/does not support the attribute + } + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesGetPosixFilePermissions() throws IOException { + try { + Files.getPosixFilePermissions(readFile()); + } catch (UnsupportedOperationException | IllegalArgumentException | FileSystemException e) { + // OK if the file does not have/does not support the attribute + } + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesSetPosixFilePermissions() throws IOException { + var file = EntitledActions.createTempFileForWrite(); + try { + Files.setPosixFilePermissions(file, Set.of()); + } catch (UnsupportedOperationException | IllegalArgumentException | FileSystemException e) { + // OK if the file does not have/does not support the attribute + } + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesIsSymbolicLink() { + Files.isSymbolicLink(readFile()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesIsDirectory() { + Files.isDirectory(readFile()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesIsRegularFile() { + Files.isRegularFile(readFile()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesGetLastModifiedTime() throws IOException { + Files.getLastModifiedTime(readFile()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesSetLastModifiedTime() throws IOException { + var file = EntitledActions.createTempFileForWrite(); + Files.setLastModifiedTime(file, FileTime.from(Instant.now())); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesSize() throws IOException { + Files.size(readFile()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesExists() { + Files.exists(readFile()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesNotExists() { + Files.notExists(readFile()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesIsReadable() { + Files.isReadable(readFile()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesIsWriteable() { + Files.isWritable(readFile()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesIsExecutable() { + Files.isExecutable(readFile()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesWalkFileTree() throws IOException { + Files.walkFileTree(readDir(), new FileVisitor<>() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + return FileVisitResult.SKIP_SUBTREE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + return FileVisitResult.SKIP_SUBTREE; + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { + return FileVisitResult.SKIP_SUBTREE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + return FileVisitResult.SKIP_SUBTREE; + } + }); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesWalkFileTreeWithOptions() throws IOException { + Files.walkFileTree(readDir(), Set.of(FileVisitOption.FOLLOW_LINKS), 2, new FileVisitor<>() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + return FileVisitResult.SKIP_SUBTREE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + return FileVisitResult.SKIP_SUBTREE; + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { + return FileVisitResult.SKIP_SUBTREE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + return FileVisitResult.SKIP_SUBTREE; + } + }); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesNewBufferedReader() throws IOException { + Files.newBufferedReader(readFile()).close(); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesNewBufferedReaderWithCharset() throws IOException { + Files.newBufferedReader(readFile(), Charset.defaultCharset()).close(); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesNewBufferedWriter() throws IOException { + Files.newBufferedWriter(readWriteFile(), StandardOpenOption.WRITE).close(); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesNewBufferedWriterWithCharset() throws IOException { + Files.newBufferedWriter(readWriteFile(), Charset.defaultCharset(), StandardOpenOption.WRITE).close(); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesCopyInputStream() throws IOException { + var directory = EntitledActions.createTempDirectoryForWrite(); + Files.copy(new ByteArrayInputStream("foo".getBytes(StandardCharsets.UTF_8)), directory.resolve("copied")); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesCopyOutputStream() throws IOException { + Files.copy(readFile(), new ByteArrayOutputStream()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesReadAllBytes() throws IOException { + Files.readAllBytes(readFile()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesReadString() throws IOException { + Files.readString(readFile()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesReadStringWithCharset() throws IOException { + Files.readString(readFile(), Charset.defaultCharset()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesReadAllLines() throws IOException { + Files.readAllLines(readFile()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesReadAllLinesWithCharset() throws IOException { + Files.readAllLines(readFile(), Charset.defaultCharset()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesWrite() throws IOException { + var directory = EntitledActions.createTempDirectoryForWrite(); + Files.write(directory.resolve("file"), "foo".getBytes(StandardCharsets.UTF_8)); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesWriteLines() throws IOException { + var directory = EntitledActions.createTempDirectoryForWrite(); + Files.write(directory.resolve("file"), List.of("foo")); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesWriteString() throws IOException { + var directory = EntitledActions.createTempDirectoryForWrite(); + Files.writeString(directory.resolve("file"), "foo"); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesWriteStringWithCharset() throws IOException { + var directory = EntitledActions.createTempDirectoryForWrite(); + Files.writeString(directory.resolve("file"), "foo", Charset.defaultCharset()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesList() throws IOException { + Files.list(readDir()).close(); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesWalk() throws IOException { + Files.walk(readDir()).close(); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesWalkWithDepth() throws IOException { + Files.walk(readDir(), 2).close(); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesFind() throws IOException { + Files.find(readDir(), 2, (path, basicFileAttributes) -> false).close(); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesLines() throws IOException { + Files.lines(readFile()).close(); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void checkFilesLinesWithCharset() throws IOException { + Files.lines(readFile(), Charset.defaultCharset()).close(); + } + + private NioFilesActions() {} +} diff --git a/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/RestEntitlementsCheckAction.java b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/RestEntitlementsCheckAction.java index cd9f46f06502c..a8a320226dff6 100644 --- a/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/RestEntitlementsCheckAction.java +++ b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/RestEntitlementsCheckAction.java @@ -189,6 +189,8 @@ static CheckAction alwaysDenied(CheckedRunnable action) { getTestEntries(FileStoreActions.class), getTestEntries(ManageThreadsActions.class), getTestEntries(NativeActions.class), + getTestEntries(NioChannelsActions.class), + getTestEntries(NioFilesActions.class), getTestEntries(NioFileSystemActions.class), getTestEntries(PathActions.class), getTestEntries(SpiActions.class), diff --git a/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsTestRule.java b/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsTestRule.java index 9dc1028148a31..4c1536f17ef9e 100644 --- a/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsTestRule.java +++ b/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsTestRule.java @@ -38,7 +38,7 @@ class EntitlementsTestRule implements TestRule { Map.of( "files", List.of( - Map.of("path", tempDir.resolve("read_dir"), "mode", "read"), + Map.of("path", tempDir.resolve("read_dir"), "mode", "read_write"), Map.of("path", tempDir.resolve("read_write_dir"), "mode", "read_write"), Map.of("path", tempDir.resolve("read_file"), "mode", "read"), Map.of("path", tempDir.resolve("read_write_file"), "mode", "read_write") diff --git a/libs/entitlement/src/main/java/module-info.java b/libs/entitlement/src/main/java/module-info.java index 5c8441bcecb9c..697d26747b806 100644 --- a/libs/entitlement/src/main/java/module-info.java +++ b/libs/entitlement/src/main/java/module-info.java @@ -14,6 +14,7 @@ requires org.elasticsearch.base; requires jdk.attach; requires java.net.http; + requires jdk.net; requires static org.elasticsearch.entitlement.bridge; // At runtime, this will be in java.base diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/EntitlementBootstrap.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/EntitlementBootstrap.java index 17885fb103077..5c32cdbf88f5f 100644 --- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/EntitlementBootstrap.java +++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/EntitlementBootstrap.java @@ -39,6 +39,7 @@ public record BootstrapArgs( Function, String> pluginResolver, Function settingResolver, Function> settingGlobResolver, + Function repoDirResolver, Path[] dataDirs, Path configDir, Path logsDir, @@ -49,6 +50,7 @@ public record BootstrapArgs( requireNonNull(pluginResolver); requireNonNull(settingResolver); requireNonNull(settingGlobResolver); + requireNonNull(repoDirResolver); requireNonNull(dataDirs); if (dataDirs.length == 0) { throw new IllegalArgumentException("must provide at least one data directory"); @@ -71,6 +73,9 @@ public static BootstrapArgs bootstrapArgs() { * * @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 settingResolver a functor to resolve the value of an Elasticsearch setting. + * @param settingGlobResolver a functor to resolve a glob expression for one or more Elasticsearch settings. + * @param repoDirResolver a functor to map a repository location to its Elasticsearch path. * @param dataDirs data directories for Elasticsearch * @param configDir the config directory for Elasticsearch * @param tempDir the temp directory for Elasticsearch @@ -81,6 +86,7 @@ public static void bootstrap( Function, String> pluginResolver, Function settingResolver, Function> settingGlobResolver, + Function repoDirResolver, Path[] dataDirs, Path configDir, Path logsDir, @@ -95,6 +101,7 @@ public static void bootstrap( pluginResolver, settingResolver, settingGlobResolver, + repoDirResolver, dataDirs, configDir, logsDir, diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/initialization/EntitlementInitialization.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/initialization/EntitlementInitialization.java index 8b1bdb66938f5..f531f54a0d41f 100644 --- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/initialization/EntitlementInitialization.java +++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/initialization/EntitlementInitialization.java @@ -52,7 +52,6 @@ import java.nio.file.attribute.FileAttribute; import java.nio.file.spi.FileSystemProvider; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -147,7 +146,18 @@ private static PolicyManager createPolicyManager() { List serverScopes = new ArrayList<>(); Collections.addAll( serverScopes, - new Scope("org.elasticsearch.base", List.of(new CreateClassLoaderEntitlement())), + new Scope( + "org.elasticsearch.base", + List.of( + new CreateClassLoaderEntitlement(), + new FilesEntitlement( + List.of( + FileData.ofPath(bootstrapArgs.repoDirResolver().apply(""), READ_WRITE), + FileData.ofRelativePath(Path.of(""), FilesEntitlement.BaseDir.DATA, READ_WRITE) + ) + ) + ) + ), new Scope("org.elasticsearch.xcontent", List.of(new CreateClassLoaderEntitlement())), new Scope( "org.elasticsearch.server", @@ -160,31 +170,32 @@ private static PolicyManager createPolicyManager() { new LoadNativeLibrariesEntitlement(), new ManageThreadsEntitlement(), new FilesEntitlement( - Stream.concat( - Stream.of( - FileData.ofPath(bootstrapArgs.tempDir(), READ_WRITE), - FileData.ofPath(bootstrapArgs.configDir(), READ), - FileData.ofPath(bootstrapArgs.logsDir(), READ_WRITE), - // OS release on Linux - FileData.ofPath(Path.of("/etc/os-release"), READ), - FileData.ofPath(Path.of("/etc/system-release"), READ), - FileData.ofPath(Path.of("/usr/lib/os-release"), READ), - // read max virtual memory areas - FileData.ofPath(Path.of("/proc/sys/vm/max_map_count"), READ), - FileData.ofPath(Path.of("/proc/meminfo"), READ), - // load averages on Linux - FileData.ofPath(Path.of("/proc/loadavg"), READ), - // control group stats on Linux. cgroup v2 stats are in an unpredicable - // location under `/sys/fs/cgroup`, so unfortunately we have to allow - // read access to the entire directory hierarchy. - FileData.ofPath(Path.of("/proc/self/cgroup"), READ), - FileData.ofPath(Path.of("/sys/fs/cgroup/"), 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() + List.of( + // Base ES directories + FileData.ofPath(bootstrapArgs.tempDir(), READ_WRITE), + FileData.ofPath(bootstrapArgs.configDir(), READ), + FileData.ofPath(bootstrapArgs.logsDir(), READ_WRITE), + FileData.ofRelativePath(Path.of(""), FilesEntitlement.BaseDir.DATA, READ_WRITE), + FileData.ofPath(bootstrapArgs.repoDirResolver().apply(""), READ_WRITE), + + // OS release on Linux + FileData.ofPath(Path.of("/etc/os-release"), READ), + FileData.ofPath(Path.of("/etc/system-release"), READ), + FileData.ofPath(Path.of("/usr/lib/os-release"), READ), + // read max virtual memory areas + FileData.ofPath(Path.of("/proc/sys/vm/max_map_count"), READ), + FileData.ofPath(Path.of("/proc/meminfo"), READ), + // load averages on Linux + FileData.ofPath(Path.of("/proc/loadavg"), READ), + // control group stats on Linux. cgroup v2 stats are in an unpredicable + // location under `/sys/fs/cgroup`, so unfortunately we have to allow + // read access to the entire directory hierarchy. + FileData.ofPath(Path.of("/proc/self/cgroup"), READ), + FileData.ofPath(Path.of("/sys/fs/cgroup/"), READ), + // // io stats on Linux + FileData.ofPath(Path.of("/proc/self/mountinfo"), READ), + FileData.ofPath(Path.of("/proc/diskstats"), READ) + ) ) ) ), @@ -196,13 +207,18 @@ private static PolicyManager createPolicyManager() { 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() + List.of( + FileData.ofPath(bootstrapArgs.configDir(), READ), + FileData.ofPath(bootstrapArgs.tempDir(), READ), + FileData.ofRelativePath(Path.of(""), FilesEntitlement.BaseDir.DATA, READ_WRITE) + ) ) ) ), + new Scope( + "org.apache.lucene.misc", + List.of(new FilesEntitlement(List.of(FileData.ofRelativePath(Path.of(""), FilesEntitlement.BaseDir.DATA, READ_WRITE)))) + ), new Scope("org.apache.logging.log4j.core", List.of(new ManageThreadsEntitlement())), new Scope( "org.elasticsearch.nativeaccess", @@ -226,7 +242,16 @@ private static PolicyManager createPolicyManager() { var serverPolicy = new Policy("server", serverScopes); // agents run without a module, so this is a special hack for the apm agent // this should be removed once https://github.com/elastic/elasticsearch/issues/109335 is completed - List agentEntitlements = List.of(new CreateClassLoaderEntitlement(), new ManageThreadsEntitlement()); + List agentEntitlements = List.of( + new CreateClassLoaderEntitlement(), + new ManageThreadsEntitlement(), + new FilesEntitlement( + List.of( + FileData.ofPath(Path.of("/co/elastic/apm/agent/"), READ), + FileData.ofPath(Path.of("/agent/co/elastic/apm/agent/"), READ) + ) + ) + ); var resolver = EntitlementBootstrap.bootstrapArgs().pluginResolver(); return new PolicyManager( serverPolicy, diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/api/ElasticsearchEntitlementChecker.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/api/ElasticsearchEntitlementChecker.java index 7262847602f2c..20fad76bfbfea 100644 --- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/api/ElasticsearchEntitlementChecker.java +++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/api/ElasticsearchEntitlementChecker.java @@ -9,6 +9,8 @@ package org.elasticsearch.entitlement.runtime.api; +import jdk.nio.Channels; + import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.entitlement.bridge.EntitlementChecker; import org.elasticsearch.entitlement.runtime.policy.PolicyManager; @@ -19,6 +21,7 @@ import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.io.PrintStream; import java.io.PrintWriter; import java.net.ContentHandlerFactory; @@ -57,6 +60,8 @@ import java.nio.file.CopyOption; import java.nio.file.DirectoryStream; import java.nio.file.FileStore; +import java.nio.file.FileVisitOption; +import java.nio.file.FileVisitor; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.OpenOption; @@ -64,12 +69,17 @@ import java.nio.file.StandardOpenOption; import java.nio.file.WatchEvent; import java.nio.file.WatchService; +import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.FileAttributeView; +import java.nio.file.attribute.FileTime; +import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.UserPrincipal; import java.nio.file.spi.FileSystemProvider; import java.security.KeyStore; import java.security.Provider; import java.security.cert.CertStoreParameters; +import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.Map; @@ -78,6 +88,7 @@ import java.util.TimeZone; import java.util.concurrent.ExecutorService; import java.util.concurrent.ForkJoinPool; +import java.util.function.BiPredicate; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; @@ -1254,6 +1265,72 @@ public void checkSelectorProviderInheritedChannel(Class callerClass, Selector // nio + @Override + public void check$java_nio_channels_FileChannel$(Class callerClass) { + policyManager.checkChangeFilesHandling(callerClass); + } + + @Override + public void check$java_nio_channels_FileChannel$$open( + Class callerClass, + Path path, + Set options, + FileAttribute... attrs + ) { + if (isOpenForWrite(options)) { + policyManager.checkFileWrite(callerClass, path); + } else { + policyManager.checkFileRead(callerClass, path); + } + } + + @Override + public void check$java_nio_channels_FileChannel$$open(Class callerClass, Path path, OpenOption... options) { + if (isOpenForWrite(options)) { + policyManager.checkFileWrite(callerClass, path); + } else { + policyManager.checkFileRead(callerClass, path); + } + } + + @Override + public void check$java_nio_channels_AsynchronousFileChannel$(Class callerClass) { + policyManager.checkChangeFilesHandling(callerClass); + } + + @Override + public void check$java_nio_channels_AsynchronousFileChannel$$open( + Class callerClass, + Path path, + Set options, + ExecutorService executor, + FileAttribute... attrs + ) { + if (isOpenForWrite(options)) { + policyManager.checkFileWrite(callerClass, path); + } else { + policyManager.checkFileRead(callerClass, path); + } + } + + @Override + public void check$java_nio_channels_AsynchronousFileChannel$$open(Class callerClass, Path path, OpenOption... options) { + if (isOpenForWrite(options)) { + policyManager.checkFileWrite(callerClass, path); + } else { + policyManager.checkFileRead(callerClass, path); + } + } + + @Override + public void check$jdk_nio_Channels$$readWriteSelectableChannel( + Class callerClass, + FileDescriptor fd, + Channels.SelectableChannelCloser closer + ) { + policyManager.checkFileDescriptorWrite(callerClass); + } + @Override public void check$java_nio_file_Files$$getOwner(Class callerClass, Path path, LinkOption... options) { policyManager.checkFileRead(callerClass, path); @@ -1269,6 +1346,411 @@ public void checkSelectorProviderInheritedChannel(Class callerClass, Selector policyManager.checkFileWrite(callerClass, path); } + @Override + public void check$java_nio_file_Files$$newInputStream(Class callerClass, Path path, OpenOption... options) { + policyManager.checkFileRead(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$newOutputStream(Class callerClass, Path path, OpenOption... options) { + policyManager.checkFileWrite(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$newByteChannel( + Class callerClass, + Path path, + Set options, + FileAttribute... attrs + ) { + if (isOpenForWrite(options)) { + policyManager.checkFileWrite(callerClass, path); + } else { + policyManager.checkFileRead(callerClass, path); + } + } + + @Override + public void check$java_nio_file_Files$$newByteChannel(Class callerClass, Path path, OpenOption... options) { + if (isOpenForWrite(options)) { + policyManager.checkFileWrite(callerClass, path); + } else { + policyManager.checkFileRead(callerClass, path); + } + } + + @Override + public void check$java_nio_file_Files$$newDirectoryStream(Class callerClass, Path dir) { + policyManager.checkFileRead(callerClass, dir); + } + + @Override + public void check$java_nio_file_Files$$newDirectoryStream(Class callerClass, Path dir, String glob) { + policyManager.checkFileRead(callerClass, dir); + } + + @Override + public void check$java_nio_file_Files$$newDirectoryStream(Class callerClass, Path dir, DirectoryStream.Filter filter) { + policyManager.checkFileRead(callerClass, dir); + } + + @Override + public void check$java_nio_file_Files$$createFile(Class callerClass, Path path, FileAttribute... attrs) { + policyManager.checkFileWrite(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$createDirectory(Class callerClass, Path dir, FileAttribute... attrs) { + policyManager.checkFileWrite(callerClass, dir); + } + + @Override + public void check$java_nio_file_Files$$createDirectories(Class callerClass, Path dir, FileAttribute... attrs) { + policyManager.checkFileWrite(callerClass, dir); + } + + @Override + public void check$java_nio_file_Files$$createTempFile( + Class callerClass, + Path dir, + String prefix, + String suffix, + FileAttribute... attrs + ) { + policyManager.checkFileWrite(callerClass, dir); + } + + @Override + public void check$java_nio_file_Files$$createTempFile(Class callerClass, String prefix, String suffix, FileAttribute... attrs) { + policyManager.checkCreateTempFile(callerClass); + } + + @Override + public void check$java_nio_file_Files$$createTempDirectory(Class callerClass, Path dir, String prefix, FileAttribute... attrs) { + policyManager.checkFileWrite(callerClass, dir); + } + + @Override + public void check$java_nio_file_Files$$createTempDirectory(Class callerClass, String prefix, FileAttribute... attrs) { + policyManager.checkCreateTempFile(callerClass); + } + + @Override + public void check$java_nio_file_Files$$createSymbolicLink(Class callerClass, Path link, Path target, FileAttribute... attrs) { + policyManager.checkFileRead(callerClass, target); + policyManager.checkFileWrite(callerClass, link); + } + + @Override + public void check$java_nio_file_Files$$createLink(Class callerClass, Path link, Path existing) { + policyManager.checkFileRead(callerClass, existing); + policyManager.checkFileWrite(callerClass, link); + } + + @Override + public void check$java_nio_file_Files$$delete(Class callerClass, Path path) { + policyManager.checkFileWrite(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$deleteIfExists(Class callerClass, Path path) { + policyManager.checkFileWrite(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$copy(Class callerClass, Path source, Path target, CopyOption... options) { + policyManager.checkFileRead(callerClass, source); + policyManager.checkFileWrite(callerClass, target); + } + + @Override + public void check$java_nio_file_Files$$move(Class callerClass, Path source, Path target, CopyOption... options) { + policyManager.checkFileWrite(callerClass, source); + policyManager.checkFileWrite(callerClass, target); + } + + @Override + public void check$java_nio_file_Files$$readSymbolicLink(Class callerClass, Path link) { + policyManager.checkFileRead(callerClass, link); + } + + @Override + public void check$java_nio_file_Files$$getFileStore(Class callerClass, Path path) { + policyManager.checkFileRead(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$isSameFile(Class callerClass, Path path, Path path2) { + policyManager.checkFileRead(callerClass, path); + policyManager.checkFileRead(callerClass, path2); + } + + @Override + public void check$java_nio_file_Files$$mismatch(Class callerClass, Path path, Path path2) { + policyManager.checkFileRead(callerClass, path); + policyManager.checkFileRead(callerClass, path2); + } + + @Override + public void check$java_nio_file_Files$$isHidden(Class callerClass, Path path) { + policyManager.checkFileRead(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$getFileAttributeView( + Class callerClass, + Path path, + Class type, + LinkOption... options + ) { + policyManager.checkGetFileAttributeView(callerClass); + } + + @Override + public void check$java_nio_file_Files$$readAttributes( + Class callerClass, + Path path, + Class type, + LinkOption... options + ) { + policyManager.checkFileRead(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$setAttribute( + Class callerClass, + Path path, + String attribute, + Object value, + LinkOption... options + ) { + policyManager.checkFileWrite(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$getAttribute(Class callerClass, Path path, String attribute, LinkOption... options) { + policyManager.checkFileRead(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$readAttributes(Class callerClass, Path path, String attributes, LinkOption... options) { + policyManager.checkFileRead(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$getPosixFilePermissions(Class callerClass, Path path, LinkOption... options) { + policyManager.checkFileRead(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$setPosixFilePermissions(Class callerClass, Path path, Set perms) { + policyManager.checkFileWrite(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$isSymbolicLink(Class callerClass, Path path) { + policyManager.checkFileRead(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$isDirectory(Class callerClass, Path path, LinkOption... options) { + policyManager.checkFileRead(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$isRegularFile(Class callerClass, Path path, LinkOption... options) { + policyManager.checkFileRead(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$getLastModifiedTime(Class callerClass, Path path, LinkOption... options) { + policyManager.checkFileRead(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$setLastModifiedTime(Class callerClass, Path path, FileTime time) { + policyManager.checkFileWrite(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$size(Class callerClass, Path path) { + policyManager.checkFileRead(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$exists(Class callerClass, Path path, LinkOption... options) { + policyManager.checkFileRead(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$notExists(Class callerClass, Path path, LinkOption... options) { + policyManager.checkFileRead(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$isReadable(Class callerClass, Path path) { + policyManager.checkFileRead(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$isWritable(Class callerClass, Path path) { + policyManager.checkFileRead(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$isExecutable(Class callerClass, Path path) { + policyManager.checkFileRead(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$walkFileTree( + Class callerClass, + Path start, + Set options, + int maxDepth, + FileVisitor visitor + ) { + policyManager.checkFileRead(callerClass, start); + } + + @Override + public void check$java_nio_file_Files$$walkFileTree(Class callerClass, Path start, FileVisitor visitor) { + policyManager.checkFileRead(callerClass, start); + } + + @Override + public void check$java_nio_file_Files$$newBufferedReader(Class callerClass, Path path, Charset cs) { + policyManager.checkFileRead(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$newBufferedReader(Class callerClass, Path path) { + policyManager.checkFileRead(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$newBufferedWriter(Class callerClass, Path path, Charset cs, OpenOption... options) { + policyManager.checkFileWrite(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$newBufferedWriter(Class callerClass, Path path, OpenOption... options) { + policyManager.checkFileWrite(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$copy(Class callerClass, InputStream in, Path target, CopyOption... options) { + policyManager.checkFileWrite(callerClass, target); + } + + @Override + public void check$java_nio_file_Files$$copy(Class callerClass, Path source, OutputStream out) { + policyManager.checkFileRead(callerClass, source); + } + + @Override + public void check$java_nio_file_Files$$readAllBytes(Class callerClass, Path path) { + policyManager.checkFileRead(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$readString(Class callerClass, Path path) { + policyManager.checkFileRead(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$readString(Class callerClass, Path path, Charset cs) { + policyManager.checkFileRead(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$readAllLines(Class callerClass, Path path, Charset cs) { + policyManager.checkFileRead(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$readAllLines(Class callerClass, Path path) { + policyManager.checkFileRead(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$write(Class callerClass, Path path, byte[] bytes, OpenOption... options) { + policyManager.checkFileWrite(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$write( + Class callerClass, + Path path, + Iterable lines, + Charset cs, + OpenOption... options + ) { + policyManager.checkFileWrite(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$write( + Class callerClass, + Path path, + Iterable lines, + OpenOption... options + ) { + policyManager.checkFileWrite(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$writeString(Class callerClass, Path path, CharSequence csq, OpenOption... options) { + policyManager.checkFileWrite(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$writeString( + Class callerClass, + Path path, + CharSequence csq, + Charset cs, + OpenOption... options + ) { + policyManager.checkFileWrite(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$list(Class callerClass, Path dir) { + policyManager.checkFileRead(callerClass, dir); + } + + @Override + public void check$java_nio_file_Files$$walk(Class callerClass, Path start, int maxDepth, FileVisitOption... options) { + policyManager.checkFileRead(callerClass, start); + } + + @Override + public void check$java_nio_file_Files$$walk(Class callerClass, Path start, FileVisitOption... options) { + policyManager.checkFileRead(callerClass, start); + } + + @Override + public void check$java_nio_file_Files$$find( + Class callerClass, + Path start, + int maxDepth, + BiPredicate matcher, + FileVisitOption... options + ) { + policyManager.checkFileRead(callerClass, start); + } + + @Override + public void check$java_nio_file_Files$$lines(Class callerClass, Path path, Charset cs) { + policyManager.checkFileRead(callerClass, path); + } + + @Override + public void check$java_nio_file_Files$$lines(Class callerClass, Path path) { + policyManager.checkFileRead(callerClass, path); + } + // file system providers @Override @@ -1304,6 +1786,17 @@ private static boolean isOpenForWrite(Set options) { || options.contains(StandardOpenOption.DELETE_ON_CLOSE); } + private static boolean isOpenForWrite(OpenOption... options) { + return Arrays.stream(options) + .anyMatch( + o -> o.equals(StandardOpenOption.WRITE) + || o.equals(StandardOpenOption.APPEND) + || o.equals(StandardOpenOption.CREATE) + || o.equals(StandardOpenOption.CREATE_NEW) + || o.equals(StandardOpenOption.DELETE_ON_CLOSE) + ); + } + @Override public void checkNewFileChannel( Class callerClass, diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java index 2049e7ec28690..66e44576b7452 100644 --- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java +++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java @@ -12,6 +12,7 @@ import org.elasticsearch.core.Strings; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.entitlement.bootstrap.EntitlementBootstrap; +import org.elasticsearch.entitlement.bridge.EntitlementChecker; import org.elasticsearch.entitlement.instrumentation.InstrumentationService; import org.elasticsearch.entitlement.runtime.api.NotEntitledException; import org.elasticsearch.entitlement.runtime.policy.entitlements.CreateClassLoaderEntitlement; @@ -258,6 +259,13 @@ public void checkChangeNetworkHandling(Class callerClass) { checkChangeJVMGlobalState(callerClass); } + /** + * Check for operations that can modify the way file operations are handled + */ + public void checkChangeFilesHandling(Class callerClass) { + checkChangeJVMGlobalState(callerClass); + } + @SuppressForbidden(reason = "Explicitly checking File apis") public void checkFileRead(Class callerClass, File file) { checkFileRead(callerClass, file.toPath()); @@ -310,6 +318,10 @@ public void checkFileWrite(Class callerClass, Path path) { } } + public void checkCreateTempFile(Class callerClass) { + checkFileWrite(callerClass, pathLookup.tempDir()); + } + @SuppressForbidden(reason = "Explicitly checking File apis") public void checkFileWithZipMode(Class callerClass, File file, int zipMode) { assert zipMode == OPEN_READ || zipMode == (OPEN_READ | OPEN_DELETE); @@ -548,6 +560,10 @@ private static boolean isTriviallyAllowed(Class requestingClass) { logger.debug("Entitlement trivially allowed from system module [{}]", requestingClass.getModule().getName()); return true; } + if (EntitlementChecker.class.isAssignableFrom(requestingClass)) { + logger.debug("Entitlement trivially allowed for EntitlementChecker class"); + return true; + } logger.trace("Entitlement not trivially allowed"); return false; } diff --git a/modules/analysis-common/src/main/plugin-metadata/entitlement-policy.yaml b/modules/analysis-common/src/main/plugin-metadata/entitlement-policy.yaml new file mode 100644 index 0000000000000..e59b9dd27406c --- /dev/null +++ b/modules/analysis-common/src/main/plugin-metadata/entitlement-policy.yaml @@ -0,0 +1,5 @@ +org.elasticsearch.analysis.common: + - files: + - relative_path: analysis + relative_to: config + mode: read diff --git a/modules/ingest-geoip/src/main/plugin-metadata/entitlement-policy.yaml b/modules/ingest-geoip/src/main/plugin-metadata/entitlement-policy.yaml index cef2e0cd6aeb9..0070b1978d51d 100644 --- a/modules/ingest-geoip/src/main/plugin-metadata/entitlement-policy.yaml +++ b/modules/ingest-geoip/src/main/plugin-metadata/entitlement-policy.yaml @@ -1,5 +1,10 @@ +org.elasticsearch.ingest.geoip: + - files: + - relative_path: "ingest-geoip" + relative_to: config + mode: read com.maxmind.db: - files: - - relative_path: "ingest-geoip/" - relative_to: "config" - mode: "read_write" + - relative_path: "ingest-geoip/" + relative_to: "config" + mode: "read_write" diff --git a/modules/ingest-user-agent/src/main/plugin-metadata/entitlement-policy.yaml b/modules/ingest-user-agent/src/main/plugin-metadata/entitlement-policy.yaml new file mode 100644 index 0000000000000..bee7f4570cc6d --- /dev/null +++ b/modules/ingest-user-agent/src/main/plugin-metadata/entitlement-policy.yaml @@ -0,0 +1,5 @@ +org.elasticsearch.ingest.useragent: + - files: + - relative_path: ingest-user-agent + relative_to: config + mode: read diff --git a/modules/reindex/src/main/plugin-metadata/entitlement-policy.yaml b/modules/reindex/src/main/plugin-metadata/entitlement-policy.yaml index 394e5e38d9f59..d1e8d1aca74dd 100644 --- a/modules/reindex/src/main/plugin-metadata/entitlement-policy.yaml +++ b/modules/reindex/src/main/plugin-metadata/entitlement-policy.yaml @@ -1,3 +1,7 @@ ALL-UNNAMED: - manage_threads - outbound_network + - files: + - relative_path: "" + relative_to: config + mode: read diff --git a/modules/repository-azure/src/main/plugin-metadata/entitlement-policy.yaml b/modules/repository-azure/src/main/plugin-metadata/entitlement-policy.yaml index f22076b360b6a..ec454ad3202d8 100644 --- a/modules/repository-azure/src/main/plugin-metadata/entitlement-policy.yaml +++ b/modules/repository-azure/src/main/plugin-metadata/entitlement-policy.yaml @@ -6,3 +6,10 @@ io.netty.common: mode: "read" - path: "/usr/lib/os-release" mode: "read" + - path: "/proc/sys/net/core/somaxconn" + mode: read +com.azure.identity: + - files: + - relative_path: "storage-azure/" #/config/storage-azure/azure-federated-token + relative_to: config + mode: read diff --git a/modules/transport-netty4/src/main/plugin-metadata/entitlement-policy.yaml b/modules/transport-netty4/src/main/plugin-metadata/entitlement-policy.yaml index 7a3f2c11d69ba..430c925add8dd 100644 --- a/modules/transport-netty4/src/main/plugin-metadata/entitlement-policy.yaml +++ b/modules/transport-netty4/src/main/plugin-metadata/entitlement-policy.yaml @@ -11,3 +11,5 @@ io.netty.common: mode: "read" - path: "/usr/lib/os-release" mode: "read" + - path: "/proc/sys/net/core/somaxconn" + mode: read diff --git a/plugins/analysis-icu/src/main/plugin-metadata/entitlement-policy.yaml b/plugins/analysis-icu/src/main/plugin-metadata/entitlement-policy.yaml new file mode 100644 index 0000000000000..7a261a774e4aa --- /dev/null +++ b/plugins/analysis-icu/src/main/plugin-metadata/entitlement-policy.yaml @@ -0,0 +1,5 @@ +org.elasticsearch.analysis.icu: + - files: + - relative_path: "" + relative_to: config + mode: read diff --git a/plugins/repository-hdfs/src/main/plugin-metadata/entitlement-policy.yaml b/plugins/repository-hdfs/src/main/plugin-metadata/entitlement-policy.yaml index 30e61739a0633..0c921d8d61696 100644 --- a/plugins/repository-hdfs/src/main/plugin-metadata/entitlement-policy.yaml +++ b/plugins/repository-hdfs/src/main/plugin-metadata/entitlement-policy.yaml @@ -5,3 +5,7 @@ ALL-UNNAMED: - write_system_properties: properties: - hadoop.home.dir + - files: + - relative_path: "repository-hdfs/" + relative_to: config + mode: read_write diff --git a/plugins/store-smb/src/main/plugin-metadata/entitlement-policy.yaml b/plugins/store-smb/src/main/plugin-metadata/entitlement-policy.yaml new file mode 100644 index 0000000000000..1022253171a11 --- /dev/null +++ b/plugins/store-smb/src/main/plugin-metadata/entitlement-policy.yaml @@ -0,0 +1,5 @@ +ALL-UNNAMED: + - files: + - relative_path: "indices/" + relative_to: data + mode: read_write diff --git a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java index fb390f7d276d3..e8d4729e4b5c9 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java @@ -241,6 +241,7 @@ private static void initPhase2(Bootstrap bootstrap) throws IOException { pluginsResolver::resolveClassToPluginName, nodeEnv.settings()::get, nodeEnv.settings()::getGlobValues, + nodeEnv::resolveRepoDir, nodeEnv.dataDirs(), nodeEnv.configDir(), nodeEnv.logsDir(), diff --git a/x-pack/plugin/blob-cache/src/main/plugin-metadata/entitlement-policy.yaml b/x-pack/plugin/blob-cache/src/main/plugin-metadata/entitlement-policy.yaml index 34bac24411614..c24a99d12136f 100644 --- a/x-pack/plugin/blob-cache/src/main/plugin-metadata/entitlement-policy.yaml +++ b/x-pack/plugin/blob-cache/src/main/plugin-metadata/entitlement-policy.yaml @@ -1,5 +1,5 @@ org.elasticsearch.blobcache: - files: - - relative_path: "shared_snapshot_cache" - relative_to: "data" - mode: "read_write" + - relative_path: "" + relative_to: data + mode: read_write diff --git a/x-pack/plugin/core/src/main/plugin-metadata/entitlement-policy.yaml b/x-pack/plugin/core/src/main/plugin-metadata/entitlement-policy.yaml index a069e1b4ce4ce..f543e39972a78 100644 --- a/x-pack/plugin/core/src/main/plugin-metadata/entitlement-policy.yaml +++ b/x-pack/plugin/core/src/main/plugin-metadata/entitlement-policy.yaml @@ -1,3 +1,13 @@ +org.elasticsearch.xcore: + - files: + - relative_path: "" + relative_to: config + mode: read +org.elasticsearch.sslconfig: + - files: + - relative_path: "" + relative_to: config + mode: read org.apache.httpcomponents.httpclient: - outbound_network # For SamlRealm - manage_threads diff --git a/x-pack/plugin/identity-provider/src/main/plugin-metadata/entitlement-policy.yaml b/x-pack/plugin/identity-provider/src/main/plugin-metadata/entitlement-policy.yaml index ee6094d2ffef2..c5e32cf27b1e8 100644 --- a/x-pack/plugin/identity-provider/src/main/plugin-metadata/entitlement-policy.yaml +++ b/x-pack/plugin/identity-provider/src/main/plugin-metadata/entitlement-policy.yaml @@ -3,3 +3,7 @@ ALL-UNNAMED: - write_system_properties: properties: - org.apache.xml.security.ignoreLineBreaks + - files: + - relative_path: "" + relative_to: config + mode: read diff --git a/x-pack/plugin/ml/src/main/plugin-metadata/entitlement-policy.yaml b/x-pack/plugin/ml/src/main/plugin-metadata/entitlement-policy.yaml index ff8f2a8f73eac..664b7d83315de 100644 --- a/x-pack/plugin/ml/src/main/plugin-metadata/entitlement-policy.yaml +++ b/x-pack/plugin/ml/src/main/plugin-metadata/entitlement-policy.yaml @@ -1,2 +1,9 @@ org.elasticsearch.ml: - manage_threads + - files: + - relative_path: mlmodel.conf + relative_to: config + mode: read + - relative_path: "ml-local-data/" + relative_to: data + mode: read_write diff --git a/x-pack/plugin/searchable-snapshots/src/main/plugin-metadata/entitlement-policy.yaml b/x-pack/plugin/searchable-snapshots/src/main/plugin-metadata/entitlement-policy.yaml new file mode 100644 index 0000000000000..69eead6707114 --- /dev/null +++ b/x-pack/plugin/searchable-snapshots/src/main/plugin-metadata/entitlement-policy.yaml @@ -0,0 +1,8 @@ +org.elasticsearch.searchablesnapshots: + - files: + - relative_path: snapshot_cache + relative_to: data + mode: read_write + - relative_path: indices + relative_to: data + mode: read_write diff --git a/x-pack/plugin/security/src/main/plugin-metadata/entitlement-policy.yaml b/x-pack/plugin/security/src/main/plugin-metadata/entitlement-policy.yaml index 1897e826313a6..99dd7d5c1380f 100644 --- a/x-pack/plugin/security/src/main/plugin-metadata/entitlement-policy.yaml +++ b/x-pack/plugin/security/src/main/plugin-metadata/entitlement-policy.yaml @@ -1,5 +1,9 @@ org.elasticsearch.security: - set_https_connection_properties # for CommandLineHttpClient + - files: + - relative_path: "" + relative_to: config + mode: read io.netty.transport: - manage_threads - inbound_network @@ -13,6 +17,8 @@ io.netty.common: mode: "read" - path: "/usr/lib/os-release" mode: "read" + - path: "/proc/sys/net/core/somaxconn" + mode: read org.opensaml.xmlsec.impl: - write_system_properties: properties: