bitflicker64 commented on code in PR #2945: URL: https://github.com/apache/hugegraph/pull/2945#discussion_r4277450952
########## hugegraph-server/hugegraph-dist/src/assembly/static/bin/arthas.sh: ########## @@ -0,0 +1,60 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF 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. +# +# Offline attachment to a running Server or Store using the bundled agent. +set -euo pipefail + +fail() { echo "Error: $*" >&2; exit 1; } +usage() { + echo "Usage: $0 [--pid PID] [--telnet-port PORT] [--command COMMAND]" + echo "Run as the service OS user; defaults to bin/pid and loopback port 3658." + echo "New agents disable HTTP/MCP; use the existing telnet port for an active agent." + echo "Use 'quit' to disconnect, 'stop' to stop Arthas." +} + +BIN=$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd) +TOP=$(cd -P "$BIN/.." && pwd) +pid="" +port=3658 +boot_args=() +while [ "$#" -gt 0 ]; do + case "$1" in + --pid|--telnet-port|--command) + [ "$#" -ge 2 ] || fail "Missing value for $1" + case "$1" in + --pid) pid=$2 ;; + --telnet-port) port=$2 ;; + --command) boot_args=(--command "$2") ;; + esac + shift 2 ;; + -h|--help) usage; exit 0 ;; + *) usage >&2; fail "Unknown option: $1" ;; + esac +done +if [ -z "$pid" ]; then + [ -r "$BIN/pid" ] || fail "No bin/pid; start the service or supply --pid PID" + pid=$(cat "$BIN/pid") +fi +[[ "$pid" =~ ^[1-9][0-9]{0,9}$ ]] || fail "PID must be a positive integer" +[[ "$port" =~ ^[1-9][0-9]{0,4}$ ]] && [ "$port" -le 65535 ] || fail "Invalid telnet port" +JAVA=${JAVA_HOME:+$JAVA_HOME/bin/}java +ARTHAS="$TOP/lib/arthas" +[ -f "$ARTHAS/arthas-boot.jar" ] || fail "Bundled Arthas files are missing from lib/arthas" +"$JAVA" --add-modules jdk.attach --class-path "$ARTHAS/arthas-core.jar" \ Review Comment: Important: `bin/arthas.sh` cannot run in any of the official Docker images, so after this PR a containerised Store has no working Arthas path at all. All runtime images use `eclipse-temurin:17-jre-jammy` (`hugegraph-server/Dockerfile`, `Dockerfile-hstore`, `hugegraph-store/Dockerfile`, line 38). That JRE has neither `jdk.attach` nor `jdk.compiler`. This line needs both: `--add-modules jdk.attach` for `VirtualMachine`, and the source-file launcher (which compiles `ArthasAttach.java`) needs `jdk.compiler`. `arthas-boot`/`arthas-core` also attach through `jdk.attach`. I checked the image directly: ``` $ docker run --rm eclipse-temurin:17-jre-jammy sh -c 'java --list-modules | grep -E "jdk.attach|jdk.compiler"; ls $JAVA_HOME/bin; java --add-modules jdk.attach /tmp/A.java' java jfr jrunscript keytool rmiregistry Error occurred during initialization of boot layer java.lang.module.FindException: Module jdk.attach not found ``` Today `GET /v1/arthasstart` is the only way to start Arthas on a Store. It now always returns 403, and `docs/server-runtime.md` and the Store `application.yml` comments point users to `bin/arthas.sh` instead. In the Docker image and the Helm chart, Store users lose Arthas with no replacement that works. Server users in containers keep only the authenticated `PUT /arthas` path. The CI check (`test-arthas-cli.sh`) runs on the runner's full JDK, so it does not catch this. Please either make the documented replacement work in the shipped images (for example a JDK runtime base, or at least the `jdk.attach` module plus a precompiled `ArthasAttach` class so `jdk.compiler` is not needed), or state in the docs that the images ship a JRE and give a supported container procedure, such as an ephemeral JDK debug container sharing the target's PID namespace. ########## hugegraph-server/hugegraph-dist/src/assembly/static/bin/ArthasAttach.java: ########## @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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. + */ + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Properties; +import java.util.regex.Pattern; + +import com.sun.tools.attach.VirtualMachine; +import com.taobao.arthas.core.config.FeatureCodec; + +class ArthasAttach { + public static void main(String[] args) throws Exception { + Path home = Path.of(args[1]); + Path top = Path.of(args[3]); + VirtualMachine vm = VirtualMachine.attach(args[0]); + Path configFile = null; + try { + Properties target = vm.getSystemProperties(); + String command = target.getProperty("sun.java.command", ""); + String main = "org.apache.hugegraph.bootstrap.HugeGraphServerBootstrap"; + boolean server = Files.isRegularFile(top.resolve("bin/start-hugegraph.sh")) && + (command.equals(main) || command.startsWith(main + " ")); + boolean store = Files.isRegularFile(top.resolve("bin/start-hugegraph-store.sh")) && + (command.matches(Pattern.quote(top.resolve("lib/hg-store-node-").toString()) + + "[^/]+[.]jar(?: .*|$)") || + (command.matches("org[.]springframework[.]boot[.]loader[.]JarLauncher(?: .*|$)") && + "HugeGraphStore".equals(target.getProperty("name")))); + if (!server && !store) { + throw new IllegalArgumentException("PID does not identify a Server or Store in this distribution"); + } + // FeatureCodec drops blank values. Pin a private properties file + // first so target system/env configuration cannot restore MCP/tunnel. + configFile = Files.createTempFile("hugegraph-arthas-", ".properties"); + Files.writeString(configFile, "arthas.config.overrideAll=true\n" + Review Comment: Important: the agent this helper starts accepts commands from any local process with no credentials, and it keeps listening after the operator disconnects. The private config written here sets only `arthas.ip`, the ports, `sessionTimeout`, and the MCP/tunnel blanks. It sets no `arthas.username`/`arthas.password`, and `arthas.sh` line 59 passes none to `arthas-boot`. The PR's own CI shows that no authentication is required: `test-arthas-cli.sh` connects with the plain client and runs `version` and `stop`. `--command` mode and `quit` both leave the agent running (the usage text says only `stop` stops it), and `sessionTimeout=1800` closes idle sessions, not the listener. So after one `bin/arthas.sh --command version`, `127.0.0.1:3658` stays open. Any other OS account on the host, or any other container in the same Kubernetes Pod (Pods share loopback), can connect with `telnet` or `arthas-client` and run `ognl`/`vmtool`/`redefine` inside the Server or Store JVM as the service user. The PR description and `docs/server-runtime.md` describe access as gated by "OS attach permissions" for "the same OS user that owns the JVM", but this listener skips that check. Please gate the listener. One option: generate a one-time random password in `ArthasAttach`, write it to this private config as `arthas.password` (with `arthas.username`), and hand it to `arthas-boot` in a way that does not show up in `ps`. Another: stop the agent automatically when the helper's client exits, at least in `--command` mode. In either case, the docs should say that a running agent can be reached by any local process until it is stopped. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
