Remove JNI permissions, improve JNI testing.

This commit is contained in:
Robert Muir 2015-05-04 12:30:03 -04:00
parent 02da246b10
commit b8efa8ea21
8 changed files with 105 additions and 88 deletions

View File

@ -635,6 +635,9 @@
<tests.security.manager>${tests.security.manager}</tests.security.manager> <tests.security.manager>${tests.security.manager}</tests.security.manager>
<tests.compatibility>${tests.compatibility}</tests.compatibility> <tests.compatibility>${tests.compatibility}</tests.compatibility>
<java.awt.headless>true</java.awt.headless> <java.awt.headless>true</java.awt.headless>
<!-- true if we are running tests from maven (as opposed to IDE, etc).
allows us to assert certain things work, like libsigar -->
<tests.maven>true</tests.maven>
<!-- security manager / test.policy --> <!-- security manager / test.policy -->
<java.security.policy>${basedir}/src/main/resources/org/elasticsearch/bootstrap/security.policy</java.security.policy> <java.security.policy>${basedir}/src/main/resources/org/elasticsearch/bootstrap/security.policy</java.security.policy>
</systemProperties> </systemProperties>

View File

@ -19,6 +19,7 @@
package org.elasticsearch.bootstrap; package org.elasticsearch.bootstrap;
import org.apache.lucene.util.StringHelper;
import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.Version; import org.elasticsearch.Version;
import org.elasticsearch.common.PidFile; import org.elasticsearch.common.PidFile;
@ -27,6 +28,7 @@ import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.inject.CreationException; import org.elasticsearch.common.inject.CreationException;
import org.elasticsearch.common.inject.spi.Message; import org.elasticsearch.common.inject.spi.Message;
import org.elasticsearch.common.io.PathUtils; import org.elasticsearch.common.io.PathUtils;
import org.elasticsearch.common.jna.Kernel32Library;
import org.elasticsearch.common.jna.Natives; import org.elasticsearch.common.jna.Natives;
import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.logging.Loggers;
@ -38,6 +40,7 @@ import org.elasticsearch.monitor.process.JmxProcessProbe;
import org.elasticsearch.node.Node; import org.elasticsearch.node.Node;
import org.elasticsearch.node.NodeBuilder; import org.elasticsearch.node.NodeBuilder;
import org.elasticsearch.node.internal.InternalSettingsPreparer; import org.elasticsearch.node.internal.InternalSettingsPreparer;
import org.hyperic.sigar.Sigar;
import java.util.Locale; import java.util.Locale;
import java.util.Set; import java.util.Set;
@ -58,23 +61,15 @@ public class Bootstrap {
private static volatile CountDownLatch keepAliveLatch; private static volatile CountDownLatch keepAliveLatch;
private static Bootstrap bootstrap; private static Bootstrap bootstrap;
private void setup(boolean addShutdownHook, Settings settings, Environment environment) throws Exception { /** initialize native resources */
if (settings.getAsBoolean("bootstrap.mlockall", false)) { public static void initializeNatives(boolean mlockAll, boolean ctrlHandler) {
// mlockall if requested
if (mlockAll) {
Natives.tryMlockall(); Natives.tryMlockall();
} }
NodeBuilder nodeBuilder = NodeBuilder.nodeBuilder().settings(settings).loadConfigSettings(false); // listener for windows close event
node = nodeBuilder.build(); if (ctrlHandler) {
if (addShutdownHook) {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
node.close();
}
});
}
if (settings.getAsBoolean("bootstrap.ctrlhandler", true)) {
Natives.addConsoleCtrlHandler(new ConsoleCtrlHandler() { Natives.addConsoleCtrlHandler(new ConsoleCtrlHandler() {
@Override @Override
public boolean handle(int code) { public boolean handle(int code) {
@ -89,7 +84,36 @@ public class Bootstrap {
} }
}); });
} }
// install SM after natives, JNA can require strange permissions Kernel32Library.getInstance();
// initialize sigar explicitly
try {
Sigar.load();
Loggers.getLogger(Bootstrap.class).trace("sigar libraries loaded successfully");
} catch (Throwable t) {
Loggers.getLogger(Bootstrap.class).trace("failed to load sigar libraries", t);
}
// init lucene random seed. it will use /dev/urandom where available:
StringHelper.randomId();
}
private void setup(boolean addShutdownHook, Settings settings, Environment environment) throws Exception {
initializeNatives(settings.getAsBoolean("bootstrap.mlockall", false),
settings.getAsBoolean("bootstrap.ctrlhandler", true));
NodeBuilder nodeBuilder = NodeBuilder.nodeBuilder().settings(settings).loadConfigSettings(false);
node = nodeBuilder.build();
if (addShutdownHook) {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
node.close();
}
});
}
// install SM after natives, shutdown hooks, etc.
setupSecurity(settings, environment); setupSecurity(settings, environment);
} }

View File

@ -19,7 +19,6 @@
package org.elasticsearch.bootstrap; package org.elasticsearch.bootstrap;
import org.apache.lucene.util.StringHelper;
import org.elasticsearch.env.Environment; import org.elasticsearch.env.Environment;
import java.io.*; import java.io.*;
@ -49,9 +48,6 @@ class Security {
* Can only happen once! * Can only happen once!
*/ */
static void configure(Environment environment) throws Exception { static void configure(Environment environment) throws Exception {
// init lucene random seed. it will use /dev/urandom where available:
StringHelper.randomId();
// enable security policy: union of template and environment-based paths. // enable security policy: union of template and environment-based paths.
URI template = Security.class.getResource(POLICY_RESOURCE).toURI(); URI template = Security.class.getResource(POLICY_RESOURCE).toURI();
Policy.setPolicy(new ESPolicy(template, createPermissions(environment))); Policy.setPolicy(new ESPolicy(template, createPermissions(environment)));

View File

@ -22,6 +22,8 @@ package org.elasticsearch.common.jna;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.sun.jna.Native; import com.sun.jna.Native;
import com.sun.jna.win32.StdCallLibrary; import com.sun.jna.win32.StdCallLibrary;
import org.apache.lucene.util.Constants;
import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.logging.Loggers;
@ -46,6 +48,7 @@ public class Kernel32Library {
} }
private Kernel32Library() { private Kernel32Library() {
if (Constants.WINDOWS) {
try { try {
Native.register("kernel32"); Native.register("kernel32");
logger.debug("windows/Kernel32 library loaded"); logger.debug("windows/Kernel32 library loaded");
@ -55,6 +58,7 @@ public class Kernel32Library {
logger.warn("unable to link Windows/Kernel32 library. native methods and handlers will be disabled."); logger.warn("unable to link Windows/Kernel32 library. native methods and handlers will be disabled.");
} }
} }
}
public static Kernel32Library getInstance() { public static Kernel32Library getInstance() {
return Holder.instance; return Holder.instance;

View File

@ -82,9 +82,6 @@ grant {
// needed by groovy scripting // needed by groovy scripting
permission java.lang.RuntimePermission "getProtectionDomain"; permission java.lang.RuntimePermission "getProtectionDomain";
// needed for natives calls
permission java.lang.RuntimePermission "loadLibrary.*";
// reflection hacks: // reflection hacks:
// needed for Striped64 (what is this doing), also enables unmap hack // needed for Striped64 (what is this doing), also enables unmap hack
permission java.lang.RuntimePermission "accessClassInPackage.sun.misc"; permission java.lang.RuntimePermission "accessClassInPackage.sun.misc";

View File

@ -20,83 +20,31 @@
package org.elasticsearch.common.jna; package org.elasticsearch.common.jna;
import org.apache.lucene.util.Constants; import org.apache.lucene.util.Constants;
import org.elasticsearch.common.jna.Kernel32Library.ConsoleCtrlHandler;
import org.elasticsearch.test.ElasticsearchTestCase; import org.elasticsearch.test.ElasticsearchTestCase;
import org.junit.After;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
public class NativesTests extends ElasticsearchTestCase { public class NativesTests extends ElasticsearchTestCase {
/**
* Those properties are set by the JNA Api and if not ignored,
* lead to tests failure (see AbstractRandomizedTest#IGNORED_INVARIANT_PROPERTIES)
*/
private static final String[] JNA_INVARIANT_PROPERTIES = {
"jna.platform.library.path",
"jnidispatch.path"
};
private Map<String, String> properties = new HashMap<>();
@Before
public void saveProperties() {
assumeTrue("Natives can't load libraries from path if security manager is enabled.", System.getSecurityManager() == null);
for (String p : JNA_INVARIANT_PROPERTIES) {
properties.put(p, System.getProperty(p));
}
}
@After
public void restoreProperties() {
for (String p : JNA_INVARIANT_PROPERTIES) {
if (properties.get(p) != null) {
System.setProperty(p, properties.get(p));
} else {
System.clearProperty(p);
}
}
}
@Test @Test
public void testTryMlockall() { public void testMlockall() {
Natives.tryMlockall();
if (Constants.WINDOWS) { if (Constants.WINDOWS) {
assertFalse("Memory locking is not available on Windows platforms", Natives.LOCAL_MLOCKALL); assertFalse("Memory locking is not available on Windows platforms", Natives.LOCAL_MLOCKALL);
} }
if (Constants.MAC_OS_X) {
assertFalse("Memory locking is not available on OS X platforms", Natives.LOCAL_MLOCKALL);
}
} }
@Test @Test
public void testAddConsoleCtrlHandler() { public void testConsoleCtrlHandler() {
ConsoleCtrlHandler handler = new ConsoleCtrlHandler() {
@Override
public boolean handle(int code) {
return false;
}
};
Natives.addConsoleCtrlHandler(handler);
if (Constants.WINDOWS) { if (Constants.WINDOWS) {
assertNotNull(Kernel32Library.getInstance()); assertNotNull(Kernel32Library.getInstance());
assertThat(Kernel32Library.getInstance().getCallbacks().size(), equalTo(1)); assertThat(Kernel32Library.getInstance().getCallbacks().size(), equalTo(1));
} else { } else {
assertNotNull(Kernel32Library.getInstance()); assertNotNull(Kernel32Library.getInstance());
assertThat(Kernel32Library.getInstance().getCallbacks().size(), equalTo(0)); assertThat(Kernel32Library.getInstance().getCallbacks().size(), equalTo(0));
try {
Kernel32Library.getInstance().addConsoleCtrlHandler(handler);
fail("should have thrown an unsupported operation exception");
} catch (UnsatisfiedLinkError e) {
// UnsatisfiedLinkError is expected
}
} }
} }
} }

View File

@ -0,0 +1,42 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.monitor;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.hyperic.sigar.Sigar;
public class SigarTests extends ElasticsearchTestCase {
@Override
public void setUp() throws Exception {
super.setUp();
assumeTrue("we can only ensure sigar is working when running from maven",
Boolean.parseBoolean(System.getProperty("tests.maven")));
}
public void testSigarLoads() throws Exception {
Sigar.load();
}
public void testSigarWorks() throws Exception {
Sigar sigar = new Sigar();
assertNotNull(sigar.getCpu());
}
}

View File

@ -20,6 +20,7 @@
package org.elasticsearch.test; package org.elasticsearch.test;
import org.apache.lucene.util.TestSecurityManager; import org.apache.lucene.util.TestSecurityManager;
import org.elasticsearch.bootstrap.Bootstrap;
import static com.carrotsearch.randomizedtesting.RandomizedTest.systemPropertyAsBoolean; import static com.carrotsearch.randomizedtesting.RandomizedTest.systemPropertyAsBoolean;
@ -33,6 +34,8 @@ import static com.carrotsearch.randomizedtesting.RandomizedTest.systemPropertyAs
class SecurityHack { class SecurityHack {
static { static {
// just like bootstrap, initialize natives, then SM
Bootstrap.initializeNatives(true, true);
// for IDEs, we check that security.policy is set // for IDEs, we check that security.policy is set
if (systemPropertyAsBoolean("tests.security.manager", true) && if (systemPropertyAsBoolean("tests.security.manager", true) &&
System.getProperty("java.security.policy") != null) { System.getProperty("java.security.policy") != null) {