From 9b10cbe9b7a8042a8b0d6fb4404c42efd402a294 Mon Sep 17 00:00:00 2001 From: Ng Yat Yan Date: Sun, 10 Nov 2024 20:35:52 +0800 Subject: [PATCH] V1.5.0 Integrate JMX Tech for future development --- misc/start-jar.sh | 4 +- pom.xml | 2 +- .../sshd/service/JmxClientService.java | 87 +++++++++++++++++++ .../example/sshd/service/ReplyService.java | 17 ++++ 4 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/example/sshd/service/JmxClientService.java diff --git a/misc/start-jar.sh b/misc/start-jar.sh index b6ec133..d35f33c 100644 --- a/misc/start-jar.sh +++ b/misc/start-jar.sh @@ -2,12 +2,10 @@ PID=`ps -ef | grep echo-sshd-server.jar | grep -v grep | awk '{print $2}'` if [ -z "$PID" ] then - nohup authbind /opt/jdk-17/bin/java -jar echo-sshd-server.jar conf > logs/exec.log 2>&1 & + nohup authbind /opt/graalvm-jdk-17/bin/java -Dcom.sun.management.jmxremote -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.port=2020 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -jar echo-sshd-server.jar conf > logs/exec.log 2>&1 & sleep 1 PID=`ps -ef | grep echo-sshd-server.jar | grep -v grep | awk '{print $2}'` echo "started echo-sshd-server.jar pid: $PID" else echo "already exist echo-sshd-server.jar pid: $PID" fi - - diff --git a/pom.xml b/pom.xml index 1d66410..d0f4e0d 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.example.sshd echo-sshd-server - 1.4.6 + 1.5.0 ECHO SSH SERVER Learning Apache Mina SSHD library diff --git a/src/main/java/com/example/sshd/service/JmxClientService.java b/src/main/java/com/example/sshd/service/JmxClientService.java new file mode 100644 index 0000000..e3ddad6 --- /dev/null +++ b/src/main/java/com/example/sshd/service/JmxClientService.java @@ -0,0 +1,87 @@ +package com.example.sshd.service; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import javax.management.Attribute; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanInfo; +import javax.management.MBeanServerConnection; +import javax.management.ObjectName; +import javax.management.openmbean.CompositeData; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXServiceURL; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +@Service +public class JmxClientService { + + private static final Logger logger = LoggerFactory.getLogger(ReplyService.class); + + public String process(String[] args) { + // Example + // 1st parameter: service:jmx:rmi:///jndi/rmi://127.0.0.1:2020/jmxrmi + // 2nd parameter: java.lang:type=Memory + + StringBuilder output = new StringBuilder(); + try { + if (args.length > 2) { + Runtime.getRuntime().freeMemory(); + System.out.println("Connection to JMX kafka..."); + JMXServiceURL url = new JMXServiceURL(args[1]); + JMXConnector jmxc = JMXConnectorFactory.connect(url, null); + MBeanServerConnection mbsc = jmxc.getMBeanServerConnection(); + + ObjectName mbeanName = new ObjectName(args[2]); + + MBeanInfo info = mbsc.getMBeanInfo(mbeanName); + MBeanAttributeInfo[] attribute = info.getAttributes(); + + logger.info("[process] args.length: {}", args.length); + if (args.length > 3) { + for (MBeanAttributeInfo attr : attribute) { + List alist = mbsc.getAttributes(mbeanName, new String[] { attr.getName() }).asList(); + if (args[3].equals(attr.getName())) { + Optional> opt = alist.stream().filter(a -> a.getName().equals(args[3])) + .map(a -> toMap((CompositeData) a.getValue())).findFirst(); + if (opt.isPresent()) { + output.append(attr.getName() + ": " + opt.get() + "\r\n"); + } + } + } + } else { + for (MBeanAttributeInfo attr : attribute) { + List alist = mbsc.getAttributes(mbeanName, new String[] { attr.getName() }).asList(); + output.append(attr.getName() + ": " + alist + "\r\n"); + } + } + jmxc.close(); + } else { + output.append("Example: jmx_client service:jmx:rmi:///jndi/rmi://127.0.0.1:2020/jmxrmi java.lang:type=Memory HeapMemoryUsage\r\n"); + } + } catch (Exception e) { + logger.error("process cmd failed: {}", Arrays.asList(args), e); + } + return output.toString(); + } + + public static Map toMap(CompositeData cd) { + if (cd == null) + throw new IllegalArgumentException("composite data should not be null"); + Map map = new HashMap(); + Set itemNames = cd.getCompositeType().keySet(); + for (String itemName : itemNames) { + Object item = cd.get(itemName); + map.put(itemName, item); + } + return map; + } +} diff --git a/src/main/java/com/example/sshd/service/ReplyService.java b/src/main/java/com/example/sshd/service/ReplyService.java index 0a4f02c..fe5f4bd 100644 --- a/src/main/java/com/example/sshd/service/ReplyService.java +++ b/src/main/java/com/example/sshd/service/ReplyService.java @@ -42,6 +42,9 @@ public class ReplyService { @Autowired GeoIpLocator geoIpLocator; + + @Autowired + JmxClientService jmxClientService; public boolean replyToCommand(String command, OutputStream out, String prompt, ServerSession session) throws IOException { @@ -51,12 +54,20 @@ public class ReplyService { logger.info("[{}] my_geolocation command detected: {}", cmdHash, command.trim()); out.write(String.format("\r\n%s\r\n%s", ipInfoMapping.get(Thread.currentThread().getName()), prompt) .getBytes()); + } else if (StringUtils.equalsIgnoreCase(command.trim(), "whoami")) { logger.info("[{}] whoami command detected: {}", cmdHash, command.trim()); out.write(String.format("\r\n%s\r\n%s", session.getUsername(), prompt).getBytes()); + } else if (StringUtils.equalsIgnoreCase(command.trim(), "online_geolocations")) { logger.info("[{}] online_geolocations command detected: {}", cmdHash, command.trim()); out.write(String.format("\r\n%s\r\n%s", ipInfoMapping.toString(), prompt).getBytes()); + + } else if (StringUtils.equalsIgnoreCase(StringUtils.split(command.trim(), " ")[0], "jmx_client")) { + String remoteJmxResponse = jmxClientService.process(StringUtils.split(command.trim(), " ")); + logger.info("[{}] jmx_client command detected: {}", cmdHash, command.trim()); + out.write(String.format("\r\n%s\r\n%s", remoteJmxResponse, prompt).getBytes()); + } else if (StringUtils.split(command.trim(), " ").length == 2 && StringUtils.equalsIgnoreCase(StringUtils.split(command.trim(), " ")[0], "get_geolocation")) { String remoteIpInfo = StringUtils.getIfBlank( @@ -64,29 +75,35 @@ public class ReplyService { () -> geoIpLocator.getIpLocationInfo(StringUtils.split(command.trim(), " ")[1])); logger.info("[{}] get_geolocation command detected: {}", cmdHash, command.trim()); out.write(String.format("\r\n%s\r\n%s", remoteIpInfo, prompt).getBytes()); + } else if (StringUtils.equalsIgnoreCase(command.trim(), "all_geolocations")) { logger.info("[{}] all_geolocations command detected: {}", cmdHash, command.trim()); out.write(String.format("\r\n%s\r\n%s", jdbcService.getAllRemoteIpInfo(), prompt).getBytes()); + } else if (StringUtils.equalsIgnoreCase(command.trim(), "exit") || StringUtils.equalsIgnoreCase(command.trim(), "quit")) { logger.info("[{}] Exiting command detected: {}", cmdHash, command.trim()); out.write(String.format("\r\nExiting...\r\n%s", prompt).getBytes()); return true; + } else if (hashReplies.containsKey(command.trim())) { logger.info("[{}] Known command detected: {}", cmdHash, command.trim()); String reply = hashReplies.getProperty(command.trim()).replace("\\r", "\r").replace("\\n", "\n") .replace("\\t", "\t"); out.write(String.format("\r\n%s\r\n%s", reply, prompt).getBytes()); + } else if (hashReplies.containsKey(cmdHash)) { logger.info("[{}] Known command-hash detected: {}", cmdHash, command.trim()); String reply = hashReplies.getProperty(cmdHash).replace("\\r", "\r").replace("\\n", "\n").replace("\\t", "\t"); out.write(String.format("\r\n%s\r\n%s", reply, prompt).getBytes()); + } else if (hashReplies.containsKey(String.format("base64(%s)", cmdHash))) { logger.info("[{}] Known base64-hash detected: {}", cmdHash, command.trim()); String reply = hashReplies.getProperty(String.format("base64(%s)", cmdHash)); reply = new String(Base64.decodeBase64(reply)); out.write(String.format("\r\n%s\r\n%s", reply, prompt).getBytes()); + } else { Optional> o = regexMapping.entrySet().stream() .filter(e -> command.trim().matches(((String) e.getKey())))