diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/InternalDistributionModuleCheckTaskProvider.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/InternalDistributionModuleCheckTaskProvider.java index 92a8db6b5b91..0b71460e8d92 100644 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/InternalDistributionModuleCheckTaskProvider.java +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/InternalDistributionModuleCheckTaskProvider.java @@ -60,7 +60,6 @@ public class InternalDistributionModuleCheckTaskProvider { "org.elasticsearch.nativeaccess", "org.elasticsearch.plugin", "org.elasticsearch.plugin.analysis", - "org.elasticsearch.securesm", "org.elasticsearch.server", "org.elasticsearch.simdvec", "org.elasticsearch.tdigest", diff --git a/libs/secure-sm/build.gradle b/libs/secure-sm/build.gradle deleted file mode 100644 index d93afcf84afe..000000000000 --- a/libs/secure-sm/build.gradle +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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". - */ -apply plugin: 'elasticsearch.publish' - -dependencies { - // do not add non-test compile dependencies to secure-sm without a good reason to do so - - testImplementation "com.carrotsearch.randomizedtesting:randomizedtesting-runner:${versions.randomizedrunner}" - testImplementation "junit:junit:${versions.junit}" - testImplementation "org.hamcrest:hamcrest:${versions.hamcrest}" - - testImplementation(project(":test:framework")) { - exclude group: 'org.elasticsearch', module: 'secure-sm' - } -} - -tasks.named('forbiddenApisMain').configure { - replaceSignatureFiles 'jdk-signatures' -} - -// JAR hell is part of core which we do not want to add as a dependency -tasks.named("jarHell").configure { enabled = false } -tasks.named("testTestingConventions").configure { - baseClass 'junit.framework.TestCase' - baseClass 'org.junit.Assert' -} diff --git a/libs/secure-sm/src/main/java/module-info.java b/libs/secure-sm/src/main/java/module-info.java deleted file mode 100644 index fb3b6e357a1e..000000000000 --- a/libs/secure-sm/src/main/java/module-info.java +++ /dev/null @@ -1,12 +0,0 @@ -/* - * 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". - */ - -module org.elasticsearch.securesm { - exports org.elasticsearch.secure_sm; -} diff --git a/libs/secure-sm/src/main/java/org/elasticsearch/secure_sm/SecureSM.java b/libs/secure-sm/src/main/java/org/elasticsearch/secure_sm/SecureSM.java deleted file mode 100644 index 02d0491118dc..000000000000 --- a/libs/secure-sm/src/main/java/org/elasticsearch/secure_sm/SecureSM.java +++ /dev/null @@ -1,275 +0,0 @@ -/* - * 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.secure_sm; - -import java.security.AccessController; -import java.security.Permission; -import java.security.PrivilegedAction; -import java.util.Objects; - -/** - * Extension of SecurityManager that works around a few design flaws in Java Security. - *

- * There are a few major problems that require custom {@code SecurityManager} logic to fix: - *

- * This class throws exception on {@code exitVM} calls, and provides a whitelist where calls - * from exit are allowed. - *

- * Additionally it enforces threadgroup security with the following rules: - *

- *

- * If java security debugging ({@code java.security.debug}) is enabled, and this SecurityManager - * is installed, it will emit additional debugging information when threadgroup access checks fail. - * - * @see SecurityManager#checkAccess(Thread) - * @see SecurityManager#checkAccess(ThreadGroup) - * @see - * http://cs.oswego.edu/pipermail/concurrency-interest/2009-August/006508.html - */ -public class SecureSM extends SecurityManager { - - private final String[] classesThatCanExit; - - /** - * Creates a new security manager where no packages can exit nor halt the virtual machine. - */ - public SecureSM() { - this(new String[0]); - } - - /** - * Creates a new security manager with the specified list of regular expressions as the those that class names will be tested against to - * check whether or not a class can exit or halt the virtual machine. - * - * @param classesThatCanExit the list of classes that can exit or halt the virtual machine - */ - public SecureSM(final String[] classesThatCanExit) { - this.classesThatCanExit = classesThatCanExit; - } - - /** - * Creates a new security manager with a standard set of test packages being the only packages that can exit or halt the virtual - * machine. The packages that can exit are: - *

- * - * @return an instance of SecureSM where test packages can halt or exit the virtual machine - */ - public static SecureSM createTestSecureSM() { - return new SecureSM(TEST_RUNNER_PACKAGES); - } - - static final String[] TEST_RUNNER_PACKAGES = new String[] { - // surefire test runner - "org\\.apache\\.maven\\.surefire\\.booter\\..*", - // junit4 test runner - "com\\.carrotsearch\\.ant\\.tasks\\.junit4\\.slave\\..*", - // eclipse test runner - "org\\.eclipse.jdt\\.internal\\.junit\\.runner\\..*", - // intellij test runner (before IDEA version 2019.3) - "com\\.intellij\\.rt\\.execution\\.junit\\..*", - // intellij test runner (since IDEA version 2019.3) - "com\\.intellij\\.rt\\.junit\\..*" }; - - // java.security.debug support - private static final boolean DEBUG = AccessController.doPrivileged(new PrivilegedAction() { - @Override - public Boolean run() { - try { - String v = System.getProperty("java.security.debug"); - // simple check that they are trying to debug - return v != null && v.length() > 0; - } catch (SecurityException e) { - return false; - } - } - }); - - @Override - @SuppressForbidden(reason = "java.security.debug messages go to standard error") - public void checkAccess(Thread t) { - try { - checkThreadAccess(t); - } catch (SecurityException e) { - if (DEBUG) { - System.err.println("access: caller thread=" + Thread.currentThread()); - System.err.println("access: target thread=" + t); - debugThreadGroups(Thread.currentThread().getThreadGroup(), t.getThreadGroup()); - } - throw e; - } - } - - @Override - @SuppressForbidden(reason = "java.security.debug messages go to standard error") - public void checkAccess(ThreadGroup g) { - try { - checkThreadGroupAccess(g); - } catch (SecurityException e) { - if (DEBUG) { - System.err.println("access: caller thread=" + Thread.currentThread()); - debugThreadGroups(Thread.currentThread().getThreadGroup(), g); - } - throw e; - } - } - - @SuppressForbidden(reason = "java.security.debug messages go to standard error") - private static void debugThreadGroups(final ThreadGroup caller, final ThreadGroup target) { - System.err.println("access: caller group=" + caller); - System.err.println("access: target group=" + target); - } - - // thread permission logic - - private static final Permission MODIFY_THREAD_PERMISSION = new RuntimePermission("modifyThread"); - private static final Permission MODIFY_ARBITRARY_THREAD_PERMISSION = new ThreadPermission("modifyArbitraryThread"); - - // Returns true if the given thread is an instance of the JDK's InnocuousThread. - private static boolean isInnocuousThread(Thread t) { - final Class c = t.getClass(); - return c.getModule() == Object.class.getModule() - && (c.getName().equals("jdk.internal.misc.InnocuousThread") - || c.getName().equals("java.util.concurrent.ForkJoinWorkerThread$InnocuousForkJoinWorkerThread")); - } - - protected void checkThreadAccess(Thread t) { - Objects.requireNonNull(t); - - boolean targetThreadIsInnocuous = isInnocuousThread(t); - // we don't need to check if innocuous thread is modifying itself (like changes its name) - if (Thread.currentThread() != t || targetThreadIsInnocuous == false) { - // first, check if we can modify threads at all. - checkPermission(MODIFY_THREAD_PERMISSION); - } - - // check the threadgroup, if its our thread group or an ancestor, its fine. - final ThreadGroup source = Thread.currentThread().getThreadGroup(); - final ThreadGroup target = t.getThreadGroup(); - - if (target == null) { - return; // its a dead thread, do nothing. - } else if (source.parentOf(target) == false && targetThreadIsInnocuous == false) { - checkPermission(MODIFY_ARBITRARY_THREAD_PERMISSION); - } - } - - private static final Permission MODIFY_THREADGROUP_PERMISSION = new RuntimePermission("modifyThreadGroup"); - private static final Permission MODIFY_ARBITRARY_THREADGROUP_PERMISSION = new ThreadPermission("modifyArbitraryThreadGroup"); - - // Returns true if the given thread is an instance of the JDK's InnocuousThread. - private static boolean isInnocuousThreadGroup(ThreadGroup t) { - final Class c = t.getClass(); - return c.getModule() == Object.class.getModule() && t.getName().equals("InnocuousForkJoinWorkerThreadGroup"); - } - - protected void checkThreadGroupAccess(ThreadGroup g) { - Objects.requireNonNull(g); - - boolean targetThreadGroupIsInnocuous = isInnocuousThreadGroup(g); - - // first, check if we can modify thread groups at all. - if (targetThreadGroupIsInnocuous == false) { - checkPermission(MODIFY_THREADGROUP_PERMISSION); - } - - // check the threadgroup, if its our thread group or an ancestor, its fine. - final ThreadGroup source = Thread.currentThread().getThreadGroup(); - final ThreadGroup target = g; - - if (source == null) { - return; // we are a dead thread, do nothing - } else if (source.parentOf(target) == false && targetThreadGroupIsInnocuous == false) { - checkPermission(MODIFY_ARBITRARY_THREADGROUP_PERMISSION); - } - } - - // exit permission logic - @Override - public void checkExit(int status) { - innerCheckExit(status); - } - - /** - * The "Uwe Schindler" algorithm. - * - * @param status the exit status - */ - protected void innerCheckExit(final int status) { - AccessController.doPrivileged(new PrivilegedAction() { - @Override - public Void run() { - final String systemClassName = System.class.getName(), runtimeClassName = Runtime.class.getName(); - String exitMethodHit = null; - for (final StackTraceElement se : Thread.currentThread().getStackTrace()) { - final String className = se.getClassName(), methodName = se.getMethodName(); - if (("exit".equals(methodName) || "halt".equals(methodName)) - && (systemClassName.equals(className) || runtimeClassName.equals(className))) { - exitMethodHit = className + '#' + methodName + '(' + status + ')'; - continue; - } - - if (exitMethodHit != null) { - if (classesThatCanExit == null) { - break; - } - if (classCanExit(className, classesThatCanExit)) { - // this exit point is allowed, we return normally from closure: - return null; - } - // anything else in stack trace is not allowed, break and throw SecurityException below: - break; - } - } - - if (exitMethodHit == null) { - // should never happen, only if JVM hides stack trace - replace by generic: - exitMethodHit = "JVM exit method"; - } - throw new SecurityException(exitMethodHit + " calls are not allowed"); - } - }); - - // we passed the stack check, delegate to super, so default policy can still deny permission: - super.checkExit(status); - } - - static boolean classCanExit(final String className, final String[] classesThatCanExit) { - for (final String classThatCanExit : classesThatCanExit) { - if (className.matches(classThatCanExit)) { - return true; - } - } - return false; - } - -} diff --git a/libs/secure-sm/src/main/java/org/elasticsearch/secure_sm/SuppressForbidden.java b/libs/secure-sm/src/main/java/org/elasticsearch/secure_sm/SuppressForbidden.java deleted file mode 100644 index 6586097b5ddf..000000000000 --- a/libs/secure-sm/src/main/java/org/elasticsearch/secure_sm/SuppressForbidden.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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.secure_sm; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Annotation to suppress forbidden-apis errors inside a whole class, a method, or a field. - */ -@Retention(RetentionPolicy.CLASS) -@Target({ ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE }) -@interface SuppressForbidden { - String reason(); -} diff --git a/libs/secure-sm/src/main/java/org/elasticsearch/secure_sm/ThreadPermission.java b/libs/secure-sm/src/main/java/org/elasticsearch/secure_sm/ThreadPermission.java deleted file mode 100644 index caae4acd888e..000000000000 --- a/libs/secure-sm/src/main/java/org/elasticsearch/secure_sm/ThreadPermission.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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.secure_sm; - -import java.security.BasicPermission; - -/** - * Permission to modify threads or thread groups normally not accessible - * to the current thread. - *

- * {@link SecureSM} enforces ThreadGroup security: threads with - * {@code RuntimePermission("modifyThread")} or {@code RuntimePermission("modifyThreadGroup")} - * are only allowed to modify their current thread group or an ancestor of that group. - *

- * In some cases (e.g. test runners), code needs to manipulate arbitrary threads, - * so this Permission provides for that: the targets {@code modifyArbitraryThread} and - * {@code modifyArbitraryThreadGroup} allow a thread blanket access to any group. - * - * @see ThreadGroup - * @see SecureSM - */ -public final class ThreadPermission extends BasicPermission { - - /** - * Creates a new ThreadPermission object. - * - * @param name target name - */ - public ThreadPermission(String name) { - super(name); - } - - /** - * Creates a new ThreadPermission object. - * This constructor exists for use by the {@code Policy} object to instantiate new Permission objects. - * - * @param name target name - * @param actions ignored - */ - public ThreadPermission(String name, String actions) { - super(name, actions); - } -} diff --git a/libs/secure-sm/src/test/java/org/elasticsearch/secure_sm/SecureSMTests.java b/libs/secure-sm/src/test/java/org/elasticsearch/secure_sm/SecureSMTests.java deleted file mode 100644 index 965696d13613..000000000000 --- a/libs/secure-sm/src/test/java/org/elasticsearch/secure_sm/SecureSMTests.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * 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.secure_sm; - -import com.carrotsearch.randomizedtesting.JUnit3MethodProvider; -import com.carrotsearch.randomizedtesting.RandomizedRunner; -import com.carrotsearch.randomizedtesting.RandomizedTest; -import com.carrotsearch.randomizedtesting.annotations.TestMethodProviders; - -import org.elasticsearch.jdk.RuntimeVersionFeature; -import org.junit.BeforeClass; -import org.junit.runner.RunWith; - -import java.security.Permission; -import java.security.Policy; -import java.security.ProtectionDomain; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Collectors; - -/** Simple tests for SecureSM */ -@TestMethodProviders({ JUnit3MethodProvider.class }) -@RunWith(RandomizedRunner.class) -public class SecureSMTests extends org.junit.Assert { - - @BeforeClass - public static void initialize() { - RandomizedTest.assumeFalse( - "SecurityManager has been permanently removed in JDK 24", - RuntimeVersionFeature.isSecurityManagerAvailable() == false - ); - // install a mock security policy: - // AllPermission to source code - // ThreadPermission not granted anywhere else - final var sourceCode = Set.of(SecureSM.class.getProtectionDomain(), RandomizedRunner.class.getProtectionDomain()); - Policy.setPolicy(new Policy() { - @Override - public boolean implies(ProtectionDomain domain, Permission permission) { - if (sourceCode.contains(domain)) { - return true; - } else if (permission instanceof ThreadPermission) { - return false; - } - return true; - } - }); - System.setSecurityManager(SecureSM.createTestSecureSM()); - } - - @SuppressForbidden(reason = "testing that System#exit is blocked") - public void testTryToExit() { - try { - System.exit(1); - fail("did not hit expected exception"); - } catch (SecurityException expected) {} - } - - public void testClassCanExit() { - assertTrue(SecureSM.classCanExit("org.apache.maven.surefire.booter.CommandReader", SecureSM.TEST_RUNNER_PACKAGES)); - assertTrue(SecureSM.classCanExit("com.carrotsearch.ant.tasks.junit4.slave.JvmExit", SecureSM.TEST_RUNNER_PACKAGES)); - assertTrue(SecureSM.classCanExit("org.eclipse.jdt.internal.junit.runner.RemoteTestRunner", SecureSM.TEST_RUNNER_PACKAGES)); - assertTrue(SecureSM.classCanExit("com.intellij.rt.execution.junit.JUnitStarter", SecureSM.TEST_RUNNER_PACKAGES)); - assertTrue(SecureSM.classCanExit("org.elasticsearch.Foo", new String[] { "org.elasticsearch.Foo" })); - assertFalse(SecureSM.classCanExit("org.elasticsearch.Foo", new String[] { "org.elasticsearch.Bar" })); - } - - public void testCreateThread() throws Exception { - Thread t = new Thread(); - t.start(); - t.join(); - // no exception - } - - public void testCreateThreadGroup() throws Exception { - Thread t = new Thread(new ThreadGroup("childgroup"), "child"); - t.start(); - t.join(); - // no exception - } - - public void testModifyChild() throws Exception { - final AtomicBoolean interrupted = new AtomicBoolean(false); - Thread t = new Thread(new ThreadGroup("childgroup"), "child") { - @Override - public void run() { - try { - Thread.sleep(Long.MAX_VALUE); - } catch (InterruptedException expected) { - interrupted.set(true); - } - } - }; - t.start(); - t.interrupt(); - t.join(); - // no exception - assertTrue(interrupted.get()); - } - - public void testNoModifySibling() throws Exception { - final AtomicBoolean interrupted1 = new AtomicBoolean(false); - final AtomicBoolean interrupted2 = new AtomicBoolean(false); - - final Thread t1 = new Thread(new ThreadGroup("childgroup"), "child") { - @Override - public void run() { - try { - Thread.sleep(Long.MAX_VALUE); - } catch (InterruptedException expected) { - interrupted1.set(true); - } - } - }; - t1.start(); - - Thread t2 = new Thread(new ThreadGroup("anothergroup"), "another child") { - @Override - public void run() { - try { - Thread.sleep(Long.MAX_VALUE); - } catch (InterruptedException expected) { - interrupted2.set(true); - try { - t1.interrupt(); // try to bogusly interrupt our sibling - fail("did not hit expected exception"); - } catch (SecurityException expected2) {} - } - } - }; - t2.start(); - t2.interrupt(); - t2.join(); - // sibling attempted to but was not able to muck with its other sibling - assertTrue(interrupted2.get()); - assertFalse(interrupted1.get()); - // but we are the parent and can terminate - t1.interrupt(); - t1.join(); - assertTrue(interrupted1.get()); - } - - public void testParallelStreamThreadGroup() throws Exception { - List list = new ArrayList<>(); - for (int i = 0; i < 100; ++i) { - list.add(i); - } - list.parallelStream().collect(Collectors.toSet()); - } -} diff --git a/libs/secure-sm/src/test/java/org/elasticsearch/secure_sm/ThreadPermissionTests.java b/libs/secure-sm/src/test/java/org/elasticsearch/secure_sm/ThreadPermissionTests.java deleted file mode 100644 index 3a398e324fcc..000000000000 --- a/libs/secure-sm/src/test/java/org/elasticsearch/secure_sm/ThreadPermissionTests.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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.secure_sm; - -import junit.framework.TestCase; - -import java.security.AllPermission; - -/** - * Simple tests for ThreadPermission - */ -public class ThreadPermissionTests extends TestCase { - - public void testEquals() { - assertEquals(new ThreadPermission("modifyArbitraryThread"), new ThreadPermission("modifyArbitraryThread")); - assertFalse(new ThreadPermission("modifyArbitraryThread").equals(new AllPermission())); - assertFalse(new ThreadPermission("modifyArbitraryThread").equals(new ThreadPermission("modifyArbitraryThreadGroup"))); - } - - public void testImplies() { - assertTrue(new ThreadPermission("modifyArbitraryThread").implies(new ThreadPermission("modifyArbitraryThread"))); - assertTrue(new ThreadPermission("modifyArbitraryThreadGroup").implies(new ThreadPermission("modifyArbitraryThreadGroup"))); - assertFalse(new ThreadPermission("modifyArbitraryThread").implies(new ThreadPermission("modifyArbitraryThreadGroup"))); - assertFalse(new ThreadPermission("modifyArbitraryThreadGroup").implies(new ThreadPermission("modifyArbitraryThread"))); - assertFalse(new ThreadPermission("modifyArbitraryThread").implies(new AllPermission())); - assertFalse(new ThreadPermission("modifyArbitraryThreadGroup").implies(new AllPermission())); - assertTrue(new ThreadPermission("*").implies(new ThreadPermission("modifyArbitraryThread"))); - assertTrue(new ThreadPermission("*").implies(new ThreadPermission("modifyArbitraryThreadGroup"))); - assertFalse(new ThreadPermission("*").implies(new AllPermission())); - } -} diff --git a/server/build.gradle b/server/build.gradle index 6b406bb1d108..1b86cd639e4a 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -28,7 +28,6 @@ dependencies { api project(':libs:core') api project(':libs:logging') - api project(':libs:secure-sm') api project(':libs:x-content') api project(":libs:geo") api project(":libs:lz4") diff --git a/server/src/main/java/module-info.java b/server/src/main/java/module-info.java index 33f3d8339370..7edefe9fae58 100644 --- a/server/src/main/java/module-info.java +++ b/server/src/main/java/module-info.java @@ -23,7 +23,6 @@ module org.elasticsearch.server { requires org.elasticsearch.nativeaccess; requires org.elasticsearch.geo; requires org.elasticsearch.lz4; - requires org.elasticsearch.securesm; requires org.elasticsearch.xcontent; requires org.elasticsearch.logging; requires org.elasticsearch.plugin;