mirror of
https://github.com/apache/lucene.git
synced 2025-03-04 07:19:18 +00:00
SOLR-13993: sandbox velocity template render (if security manager is enabled)
The solr permissions are weak sauce due to the huge number of features, third-party dependencies, etc. Hence they have access to do many things. For "scripting" such as velocity we have to look at a more aggressive stance: Step 1: Can we wrap a sandbox around the whole goddamn thing and call it a day? Step 2: Let's separate the "engine" from "untrusted code" and only be an asshole to the latter. Step 3: Java's security is shit, Lets contain that classloader and whitelist access.
This commit is contained in:
parent
12e8cca644
commit
e77027dd8c
@ -166,4 +166,6 @@ grant {
|
||||
// java 8 accessibility requires this perm - should not after 8 I believe (rrd4j is the root reason we hit an accessibility code path)
|
||||
permission java.awt.AWTPermission "*";
|
||||
|
||||
// used by solr to create sandboxes (e.g. script execution)
|
||||
permission java.security.SecurityPermission "createAccessControlContext";
|
||||
};
|
||||
|
@ -17,6 +17,7 @@
|
||||
package org.apache.solr.response;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilePermission;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
@ -24,11 +25,18 @@ import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.Permissions;
|
||||
import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.PropertyPermission;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import org.apache.solr.client.solrj.SolrResponse;
|
||||
@ -147,6 +155,39 @@ public class VelocityResponseWriter implements QueryResponseWriter, SolrCoreAwar
|
||||
|
||||
@Override
|
||||
public void write(Writer writer, SolrQueryRequest request, SolrQueryResponse response) throws IOException {
|
||||
// run doWrite() with the velocity sandbox
|
||||
try {
|
||||
AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
|
||||
@Override
|
||||
public Void run() throws IOException {
|
||||
doWrite(writer, request, response);
|
||||
return null;
|
||||
}
|
||||
}, VELOCITY_SANDBOX);
|
||||
} catch (PrivilegedActionException e) {
|
||||
throw (IOException) e.getException();
|
||||
}
|
||||
}
|
||||
|
||||
// sandbox for velocity code
|
||||
// TODO: we could read in a policy file instead, in case someone needs to tweak it?
|
||||
private static final AccessControlContext VELOCITY_SANDBOX;
|
||||
static {
|
||||
Permissions permissions = new Permissions();
|
||||
// TODO: restrict the scope of this! we probably only need access to classpath
|
||||
permissions.add(new FilePermission("<<ALL FILES>>", "read,readlink"));
|
||||
// properties needed by SolrResourceLoader (called from velocity code)
|
||||
permissions.add(new PropertyPermission("jetty.testMode", "read"));
|
||||
permissions.add(new PropertyPermission("solr.allow.unsafe.resourceloading", "read"));
|
||||
// properties needed by log4j (called from velocity code)
|
||||
permissions.add(new PropertyPermission("java.version", "read"));
|
||||
// needed by velocity duck-typing
|
||||
permissions.add(new RuntimePermission("accessDeclaredMembers"));
|
||||
permissions.setReadOnly();
|
||||
VELOCITY_SANDBOX = new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, permissions) });
|
||||
}
|
||||
|
||||
private void doWrite(Writer writer, SolrQueryRequest request, SolrQueryResponse response) throws IOException {
|
||||
VelocityEngine engine = createEngine(request); // TODO: have HTTP headers available for configuring engine
|
||||
|
||||
Template template = getTemplate(engine, request);
|
||||
|
@ -0,0 +1,4 @@
|
||||
#set($x='')
|
||||
#set($sys=$x.class.forName('java.lang.System'))
|
||||
#set($ex=$sys.getProperty('os.name'))
|
||||
$ex
|
@ -0,0 +1,5 @@
|
||||
#set($x='')
|
||||
#set($sys=$x.class.forName('java.nio.file.Paths'))
|
||||
#set($path=$sys.get('/dumbass/denied_location'))
|
||||
#set($ex=$path.resolve($path).toRealPath())
|
||||
$ex
|
@ -18,6 +18,7 @@ package org.apache.solr.velocity;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.security.AccessControlException;
|
||||
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.apache.solr.common.SolrException;
|
||||
@ -27,6 +28,7 @@ import org.apache.solr.response.QueryResponseWriter;
|
||||
import org.apache.solr.response.SolrParamResourceLoader;
|
||||
import org.apache.solr.response.SolrQueryResponse;
|
||||
import org.apache.solr.response.VelocityResponseWriter;
|
||||
import org.apache.velocity.exception.MethodInvocationException;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
@ -52,6 +54,46 @@ public class VelocityResponseWriterTest extends SolrTestCaseJ4 {
|
||||
assertTrue("VrW registered check", writer instanceof VelocityResponseWriter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTemplateSandbox() throws Exception {
|
||||
assumeTrue("This test only works with security manager", System.getSecurityManager() != null);
|
||||
VelocityResponseWriter vrw = new VelocityResponseWriter();
|
||||
NamedList<String> nl = new NamedList<>();
|
||||
nl.add("template.base.dir", getFile("velocity").getAbsolutePath());
|
||||
vrw.init(nl);
|
||||
SolrQueryRequest req = req(VelocityResponseWriter.TEMPLATE,"outside_the_box");
|
||||
SolrQueryResponse rsp = new SolrQueryResponse();
|
||||
StringWriter buf = new StringWriter();
|
||||
try {
|
||||
vrw.write(buf, req, rsp);
|
||||
fail("template broke outside the box, retrieved OS: " + buf);
|
||||
} catch (MethodInvocationException e) {
|
||||
assertNotNull(e.getCause());
|
||||
assertEquals(AccessControlException.class, e.getCause().getClass());
|
||||
// expected failure, can't get outside the box
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSandboxIntersection() throws Exception {
|
||||
assumeTrue("This test only works with security manager", System.getSecurityManager() != null);
|
||||
VelocityResponseWriter vrw = new VelocityResponseWriter();
|
||||
NamedList<String> nl = new NamedList<>();
|
||||
nl.add("template.base.dir", getFile("velocity").getAbsolutePath());
|
||||
vrw.init(nl);
|
||||
SolrQueryRequest req = req(VelocityResponseWriter.TEMPLATE,"sandbox_intersection");
|
||||
SolrQueryResponse rsp = new SolrQueryResponse();
|
||||
StringWriter buf = new StringWriter();
|
||||
try {
|
||||
vrw.write(buf, req, rsp);
|
||||
fail("template broke outside the box, retrieved OS: " + buf);
|
||||
} catch (MethodInvocationException e) {
|
||||
assertNotNull(e.getCause());
|
||||
assertEquals(AccessControlException.class, e.getCause().getClass());
|
||||
// expected failure, can't get outside the box
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomParamTemplate() throws Exception {
|
||||
org.apache.solr.response.VelocityResponseWriter vrw = new VelocityResponseWriter();
|
||||
|
Loading…
x
Reference in New Issue
Block a user