Browse Source

Add system call filter bootstrap check

Today if system call filters fail to install on startup, we log a
message but otherwise march on. This might leave users without system
call filters installed not knowing that they have implicitly accepted
the additional risk. We should not be lenient like this, instead clearly
informing the user that they have to either fix their configuration or
accept the risk of not having system call filters installed. This commit
adds a bootstrap check that if system call filters are enabled, they
must successfully install.

Relates #21940
Jason Tedor 9 years ago
parent
commit
0afef53a17

+ 30 - 0
core/src/main/java/org/elasticsearch/bootstrap/BootstrapCheck.java

@@ -162,6 +162,7 @@ final class BootstrapCheck {
         }
         checks.add(new ClientJvmCheck());
         checks.add(new UseSerialGCCheck());
+        checks.add(new SystemCallFilterCheck(BootstrapSettings.SECCOMP_SETTING.get(settings)));
         checks.add(new OnErrorCheck());
         checks.add(new OnOutOfMemoryErrorCheck());
         checks.add(new G1GCCheck());
@@ -470,6 +471,35 @@ final class BootstrapCheck {
 
     }
 
+    /**
+     * Bootstrap check that if system call filters are enabled, then system call filters must have installed successfully.
+     */
+    static class SystemCallFilterCheck implements BootstrapCheck.Check {
+
+        private final boolean areSystemCallFiltersEnabled;
+
+        SystemCallFilterCheck(final boolean areSystemCallFiltersEnabled) {
+            this.areSystemCallFiltersEnabled = areSystemCallFiltersEnabled;
+        }
+
+        @Override
+        public boolean check() {
+            return areSystemCallFiltersEnabled && !isSeccompInstalled();
+        }
+
+        // visible for testing
+        boolean isSeccompInstalled() {
+            return Natives.isSeccompInstalled();
+        }
+
+        @Override
+        public String errorMessage() {
+            return "system call filters failed to install; " +
+                "check the logs and fix your configuration or disable system call filters at your own risk";
+        }
+
+    }
+
     abstract static class MightForkCheck implements BootstrapCheck.Check {
 
         @Override

+ 32 - 3
core/src/test/java/org/elasticsearch/bootstrap/BootstrapCheckTests.java

@@ -43,11 +43,8 @@ import static org.hamcrest.CoreMatchers.allOf;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.Matchers.hasToString;
-import static org.hamcrest.Matchers.not;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
@@ -411,6 +408,38 @@ public class BootstrapCheckTests extends ESTestCase {
         BootstrapCheck.check(true, Collections.singletonList(check), "testUseSerialGCCheck");
     }
 
+    public void testSystemCallFilterCheck() throws NodeValidationException {
+        final AtomicBoolean isSecompInstalled = new AtomicBoolean();
+        final BootstrapCheck.SystemCallFilterCheck systemCallFilterEnabledCheck = new BootstrapCheck.SystemCallFilterCheck(true) {
+            @Override
+            boolean isSeccompInstalled() {
+                return isSecompInstalled.get();
+            }
+        };
+
+        final NodeValidationException e = expectThrows(
+            NodeValidationException.class,
+            () -> BootstrapCheck.check(true, Collections.singletonList(systemCallFilterEnabledCheck), "testSystemCallFilterCheck"));
+        assertThat(
+            e.getMessage(),
+            containsString("system call filters failed to install; " +
+                "check the logs and fix your configuration or disable system call filters at your own risk"));
+
+        isSecompInstalled.set(true);
+        BootstrapCheck.check(true, Collections.singletonList(systemCallFilterEnabledCheck), "testSystemCallFilterCheck");
+
+        final BootstrapCheck.SystemCallFilterCheck systemCallFilterNotEnabledCheck = new BootstrapCheck.SystemCallFilterCheck(false) {
+            @Override
+            boolean isSeccompInstalled() {
+                return isSecompInstalled.get();
+            }
+        };
+        isSecompInstalled.set(false);
+        BootstrapCheck.check(true, Collections.singletonList(systemCallFilterNotEnabledCheck), "testSystemCallFilterCheck");
+        isSecompInstalled.set(true);
+        BootstrapCheck.check(true, Collections.singletonList(systemCallFilterNotEnabledCheck), "testSystemCallFilterCheck");
+    }
+
     public void testMightForkCheck() throws NodeValidationException {
         final AtomicBoolean isSeccompInstalled = new AtomicBoolean();
         final AtomicBoolean mightFork = new AtomicBoolean();

+ 12 - 0
docs/reference/setup/bootstrap-checks.asciidoc

@@ -146,6 +146,18 @@ you're using, or you've explicitly specified it with `-XX:+UseSerialGC`). Note
 that the default JVM configuration that ship with Elasticsearch configures
 Elasticsearch to use the CMS collector.
 
+=== System call filter check
+
+Elasticsearch installs system call filters of various flavors depending on the
+operating system (e.g., seccomp on Linux). These system call filters are
+installed to prevent the ability to execute system calls related to forking as
+a defense mechanism against arbitrary code execution attacks on Elasticsearch
+The system call filter check ensures that if system call filters are enabled,
+then they were successfully installed. To pass the system call filter check you
+must either fix any configuration errors on your system that prevented system
+call filters from installing (check your logs), or *at your own risk* disable
+system call filters by setting `bootstrap.seccomp` to `false`.
+
 === OnError and OnOutOfMemoryError checks
 
 The JVM options `OnError` and `OnOutOfMemoryError` enable executing