diff --git a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/config/ServerOptions.java b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/config/ServerOptions.java index 920d119d45..4aee74799f 100644 --- a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/config/ServerOptions.java +++ b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/config/ServerOptions.java @@ -439,7 +439,7 @@ public class ServerOptions extends OptionHolder { "arthas.ip", "arthas bound ip", disallowEmpty(), - "0.0.0.0" + "127.0.0.1" ); public static final ConfigOption ARTHAS_DISABLED_COMMANDS = @@ -447,7 +447,7 @@ public class ServerOptions extends OptionHolder { "arthas.disabledCommands", "arthas disabled commands", disallowEmpty(), - "jad" + "jad,ognl,vmtool" ); public static final ConfigOption ALLOW_TRACE = diff --git a/hugegraph-server/hugegraph-dist/src/assembly/static/conf/rest-server.properties b/hugegraph-server/hugegraph-dist/src/assembly/static/conf/rest-server.properties index 0dce972719..e4ddf0db97 100644 --- a/hugegraph-server/hugegraph-dist/src/assembly/static/conf/rest-server.properties +++ b/hugegraph-server/hugegraph-dist/src/assembly/static/conf/rest-server.properties @@ -12,10 +12,10 @@ batch.max_write_ratio=80 batch.max_write_threads=0 # configuration of arthas -arthas.telnet_port=8562 -arthas.http_port=8561 +arthas.telnetPort=8562 +arthas.httpPort=8561 arthas.ip=127.0.0.1 -arthas.disabled_commands=jad +arthas.disabledCommands=jad,ognl,vmtool # authentication configs #auth.authenticator=org.apache.hugegraph.auth.StandardAuthenticator diff --git a/hugegraph-store/hg-store-node/src/main/java/org/apache/hugegraph/store/node/AppConfig.java b/hugegraph-store/hg-store-node/src/main/java/org/apache/hugegraph/store/node/AppConfig.java index 3f1624c087..b1183d0f55 100644 --- a/hugegraph-store/hg-store-node/src/main/java/org/apache/hugegraph/store/node/AppConfig.java +++ b/hugegraph-store/hg-store-node/src/main/java/org/apache/hugegraph/store/node/AppConfig.java @@ -211,10 +211,10 @@ public class ArthasConfig { @Value("${arthas.httpPort:8565}") private String httpPort; - @Value("${arthas.ip:0.0.0.0}") + @Value("${arthas.ip:127.0.0.1}") private String arthasip; - @Value("${arthas.disabledCommands:jad}") + @Value("${arthas.disabledCommands:jad,ognl,vmtool}") private String disCmd; } diff --git a/hugegraph-store/hg-store-node/src/main/java/org/apache/hugegraph/store/node/controller/PartitionAPI.java b/hugegraph-store/hg-store-node/src/main/java/org/apache/hugegraph/store/node/controller/PartitionAPI.java index 34f03642ed..94e065bcb2 100644 --- a/hugegraph-store/hg-store-node/src/main/java/org/apache/hugegraph/store/node/controller/PartitionAPI.java +++ b/hugegraph-store/hg-store-node/src/main/java/org/apache/hugegraph/store/node/controller/PartitionAPI.java @@ -23,6 +23,8 @@ import java.util.List; import java.util.Map; +import javax.servlet.http.HttpServletRequest; + import org.apache.hugegraph.pd.common.PDException; import org.apache.hugegraph.pd.grpc.Metapb; import org.apache.hugegraph.rocksdb.access.RocksDBSession; @@ -37,7 +39,9 @@ import org.apache.hugegraph.store.node.grpc.HgStoreNodeService; import org.apache.hugegraph.util.Bytes; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -64,7 +68,7 @@ public class PartitionAPI { AppConfig appConfig; @GetMapping(value = "/partitions", produces = "application/json") - public Map getPartitions( + public ResponseEntity> getPartitions( @RequestParam(required = false, defaultValue = "") String flags) { boolean accurate = false; @@ -109,11 +113,11 @@ public Map getPartitions( rafts.add(raft); } - return okMap("partitions", rafts); + return ok("partitions", rafts); } @GetMapping(value = "/partition/{id}", produces = "application/json") - public Raft getPartition(@PathVariable(value = "id") int id) { + public ResponseEntity> getPartition(@PathVariable(value = "id") int id) { HgStoreEngine storeEngine = nodeService.getStoreEngine(); @@ -138,16 +142,15 @@ public Raft getPartition(@PathVariable(value = "id") int id) { raft.getPartitions().add(partition); } - return raft; - //return okMap("partition", rafts); + return ok("raft", raft); } /** * Print all keys in the partition */ @GetMapping(value = "/partition/dump/{id}", produces = MediaType.APPLICATION_JSON_VALUE) - public Map dumpPartition(@PathVariable(value = "id") int id) throws - PDException { + public ResponseEntity> dumpPartition( + @PathVariable(value = "id") int id) throws PDException { HgStoreEngine storeEngine = nodeService.getStoreEngine(); BusinessHandler handler = storeEngine.getBusinessHandler(); InnerKeyCreator innerKeyCreator = new InnerKeyCreator(handler); @@ -168,27 +171,40 @@ public Map dumpPartition(@PathVariable(value = "id") int id) thr } cfIterator.close(); }); - return okMap("ok", null); + return ok("ok", null); } /** * Print all keys in the partition */ @GetMapping(value = "/partition/clean/{id}", produces = MediaType.APPLICATION_JSON_VALUE) - public Map cleanPartition(@PathVariable(value = "id") int id) throws - PDException { + public ResponseEntity> cleanPartition( + @PathVariable(value = "id") int id) throws PDException { HgStoreEngine storeEngine = nodeService.getStoreEngine(); BusinessHandler handler = storeEngine.getBusinessHandler(); storeEngine.getPartitionEngine(id).getPartitions().forEach((graph, partition) -> { handler.cleanPartition(graph, id); }); - return okMap("ok", null); + return ok("ok", null); } @GetMapping(value = "/arthasstart", produces = "application/json") - public Map arthasstart( - @RequestParam(required = false, defaultValue = "") String flags) { + public ResponseEntity> arthasstart( + @RequestParam(required = false, defaultValue = "") String flags, + HttpServletRequest request) { + // Ignore proxy headers to prevent IP spoofing. + // NOTE: If behind a reverse proxy (e.g., Nginx), getRemoteAddr() returns the proxy's IP. + // Ensure the proxy is configured to block untrusted external access. + String remoteAddr = getCleanIp(request.getRemoteAddr()); + boolean isLocalRequest = + "127.0.0.1".equals(remoteAddr) || "0:0:0:0:0:0:0:1".equals(remoteAddr) || + "::1".equals(remoteAddr); + if (!isLocalRequest) { + List ret = new ArrayList<>(); + ret.add("Arthas start is ONLY allowed from localhost."); + return forbidden("arthasstart", ret); + } HashMap configMap = new HashMap<>(); configMap.put("arthas.telnetPort", appConfig.getArthasConfig().getTelnetPort()); configMap.put("arthas.httpPort", appConfig.getArthasConfig().getHttpPort()); @@ -198,11 +214,11 @@ public Map arthasstart( // DashResponse retPose = new DashResponse(); List ret = new ArrayList<>(); ret.add("Arthas started successfully"); - return okMap("arthasstart", ret); + return ok("arthasstart", ret); } @PostMapping("/compat") - public Map compact(@RequestParam(value = "id") int id) { + public ResponseEntity> compact(@RequestParam(value = "id") int id) { boolean submitted = nodeService.getStoreEngine().getBusinessHandler().blockingCompact("", id); Map map = new HashMap<>(); @@ -215,14 +231,28 @@ public Map compact(@RequestParam(value = "id") int id) { map.put("msg", "compaction task fail to submit, and there could be another task in progress"); } - return map; + return ok("body", map); } - public Map okMap(String k, Object v) { - Map map = new HashMap<>(); - map.put("status", 0); - map.put(k, v); - return map; + public ResponseEntity> ok(String k, Object v) { + return ResponseEntity.ok(Map.of("status", 200, k, v)); + } + + public ResponseEntity> forbidden(String k, Object v) { + return ResponseEntity.status(HttpStatus.FORBIDDEN) + .body(Map.of("status", 403, k, v)); + } + + private String getCleanIp(String remoteAddr) { + if (remoteAddr != null) { + if (remoteAddr.startsWith("[")) { + remoteAddr = remoteAddr.substring(1); + } + if (remoteAddr.endsWith("]")) { + remoteAddr = remoteAddr.substring(0, remoteAddr.length() - 1); + } + } + return remoteAddr; } @Data diff --git a/hugegraph-store/hg-store-node/src/main/resources/application.yml b/hugegraph-store/hg-store-node/src/main/resources/application.yml index 0b65270608..778af90b87 100644 --- a/hugegraph-store/hg-store-node/src/main/resources/application.yml +++ b/hugegraph-store/hg-store-node/src/main/resources/application.yml @@ -49,3 +49,10 @@ logging: config: classpath:log4j2-dev.xml level: root: info + +arthas: + telnetPort: 8566 + httpPort: 8565 + # Only allow starting arthas locally + ip: 127.0.0.1 + disabledCommands: jad,ognl,vmtool