Skip to content

Commit

Permalink
Not allowed perform sensitive operations via gremlin
Browse files Browse the repository at this point in the history
Implement #145

Change-Id: I9a590fe40d3b5a808b569ed0af8fd83214a2941a
  • Loading branch information
Linary committed Mar 15, 2019
1 parent 1e8e7bd commit d2dc070
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@

package com.baidu.hugegraph.api;

import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.inject.Singleton;
import javax.json.Json;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
Expand All @@ -41,6 +46,7 @@
import com.baidu.hugegraph.metric.MetricsUtil;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.annotation.Timed;
import com.google.common.collect.ImmutableSet;

@Path("gremlin")
@Singleton
Expand All @@ -51,6 +57,10 @@ public class GremlinAPI extends API {
private static final Histogram gremlinOutputHistogram =
MetricsUtil.registerHistogram(GremlinAPI.class, "gremlin-output");

private static final Set<String> INNER_EXCEPTIONS = ImmutableSet.of(
"java.lang.SecurityException"
);

private Client client = ClientBuilder.newClient();

private Response doGetRequest(String location, String auth, String query) {
Expand Down Expand Up @@ -107,7 +117,8 @@ public Response post(@Context HugeConfig conf,
// .build();
String location = conf.get(ServerOptions.GREMLIN_SERVER_URL);
String auth = headers.getHeaderString(HttpHeaders.AUTHORIZATION);
return doPostRequest(location, auth, request);
Response response = doPostRequest(location, auth, request);
return transformResponseIfNeed(response);
}

@GET
Expand All @@ -120,6 +131,41 @@ public Response get(@Context HugeConfig conf,
String location = conf.get(ServerOptions.GREMLIN_SERVER_URL);
String auth = headers.getHeaderString(HttpHeaders.AUTHORIZATION);
String query = uriInfo.getRequestUri().getRawQuery();
return doGetRequest(location, auth, query);
Response response = doGetRequest(location, auth, query);
return transformResponseIfNeed(response);
}

private static Response transformResponseIfNeed(Response response) {
int code = response.getStatusInfo().getStatusCode();
// No need to transform
if (code != Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()) {
return response;
}

@SuppressWarnings("unchecked")
Map<String, Object> map = response.readEntity(Map.class);
String message = (String) map.get("message");
String exClassName = (String) map.get("Exception-Class");
@SuppressWarnings("unchecked")
List<String> exceptions = (List<String>) map.get("exceptions");
if (message == null || exClassName == null || exceptions == null) {
throw new IllegalStateException(String.format(
"Invalid response for inner exception, should contains " +
"Exception-Class, but got %s", map));
}
if (INNER_EXCEPTIONS.contains(exClassName)) {
String cause = !exceptions.isEmpty() ? exceptions.get(0) : "";
String json = Json.createObjectBuilder()
.add("exception", exClassName)
.add("message", message)
.add("cause", cause)
.build().toString();
return Response.status(400)
.type(MediaType.APPLICATION_JSON)
.entity(json)
.build();
} else {
return response;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright (C) 2018 Baidu, Inc. All Rights Reserved.
*/

package com.baidu.hugegraph.exception;

import com.baidu.hugegraph.HugeException;

public class SecurityException extends HugeException {

private static final long serialVersionUID = -1427924451828873200L;

public SecurityException(String message) {
super(message);
}

public SecurityException(String message, Object... args) {
super(message, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* Copyright (C) 2018 Baidu, Inc. All Rights Reserved.
*/

package com.baidu.hugegraph.security;

import java.io.FileDescriptor;
import java.security.Permission;

import org.slf4j.Logger;

import com.baidu.hugegraph.util.Log;

public class HugeSecurityManager extends SecurityManager {

private static final Logger LOG = Log.logger(HugeSecurityManager.class);

private static final String GremlinExecutor_Class =
"org.apache.tinkerpop.gremlin.groovy.engine.ScriptEngines";

@Override
public void checkPermission(Permission permission) {
// allow anything.
}

@Override
public void checkPermission(Permission permission, Object context) {
// allow anything.
}

@Override
public void checkAccess(ThreadGroup g) {
if (this.callFromGremlin()) {
throw new SecurityException(
"Not allowed to modify thread via gremlin");
} else {
super.checkAccess(g);
}
}

@Override
public void checkExit(int status) {
if (this.callFromGremlin()) {
throw new SecurityException(
"Not allowed to call System.exit() via gremlin");
} else {
super.checkExit(status);
}
}

@Override
public void checkRead(FileDescriptor fd) {
if (this.callFromGremlin()) {
throw new SecurityException(
"Not allowed to read file via gremlin");
} else {
super.checkRead(fd);
}
}

// @Override
// public void checkRead(String file) {
// if (this.callFromGremlin()) {
// throw new SecurityException("Not allowed to read file via gremlin");
// } else {
// super.checkRead(file);
// }
// }
//
// @Override
// public void checkRead(String file, Object context) {
// if (this.callFromGremlin()) {
// throw new SecurityException("Not allowed to read file via gremlin");
// } else {
// super.checkRead(file, context);
// }
// }

@Override
public void checkWrite(FileDescriptor fd) {
if (this.callFromGremlin()) {
throw new SecurityException(
"Not allowed to write file via gremlin");
} else {
super.checkWrite(fd);
}
}

@Override
public void checkWrite(String file) {
if (this.callFromGremlin()) {
throw new SecurityException(
"Not allowed to write file via gremlin");
} else {
super.checkWrite(file);
}
}

@Override
public void checkAccept(String host, int port) {
if (this.callFromGremlin()) {
throw new SecurityException(
"Not allowed to accept connect via gremlin");
} else {
super.checkAccept(host, port);
}
}

@Override
public void checkConnect(String host, int port) {
if (this.callFromGremlin()) {
throw new SecurityException(
"Not allowed to connect socket via gremlin");
} else {
super.checkConnect(host, port);
}
}

private boolean callFromGremlin() {
StackTraceElement elements[] = Thread.currentThread().getStackTrace();
for (StackTraceElement element : elements) {
String className = element.getClassName();
if (GremlinExecutor_Class.equals(className)) {
return true;
}
}
return false;
}
}
6 changes: 5 additions & 1 deletion hugegraph-dist/src/assembly/static/bin/hugegraph-server.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ PLUGINS="$TOP/plugins"
LOG="$TOP/logs"
OUTPUT=${LOG}/hugegraph-server.log

export HUGEGRAPH_HOME="$TOP"
. ${BIN}/util.sh

ensure_path_writable $LOG
Expand Down Expand Up @@ -79,6 +80,9 @@ fi
# Execute the application and return its exit code
ARGS="conf/gremlin-server.yaml conf/rest-server.properties"

exec ${JAVA} -Dname="HugeGraphServer" -Dlog4j.configurationFile="${CONF}/log4j2.xml" \
# Turn on security check
exec ${JAVA} -Dname="HugeGraphServer" \
-Djava.security.manager="com.baidu.hugegraph.security.HugeSecurityManager" \
-Dlog4j.configurationFile="${CONF}/log4j2.xml" \
${JAVA_OPTIONS} -cp ${CLASSPATH}: com.baidu.hugegraph.dist.HugeGraphServer ${ARGS} \
>> ${OUTPUT} 2>&1
6 changes: 6 additions & 0 deletions hugegraph-dist/src/assembly/static/conf/hugegraph.policy
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Standard extensions get all permissions by default

// 目录要使用变量
grant codeBase "file:/Users/liningrui/IdeaProjects/baidu/xbu-data/hugegraph/hugegraph-0.8.0/hugegraph-core-0.8.0.jar" {
permission java.security.AllPermission;
};
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@

import javax.ws.rs.core.Response;

import org.junit.Assert;
import org.junit.Test;

import com.baidu.hugegraph.testutil.Assert;
import com.google.common.collect.ImmutableMap;

public class GremlinApiTest extends BaseApiTest {
Expand Down

0 comments on commit d2dc070

Please sign in to comment.