Подтвердить что ты не робот

Открывает ли Java 6 порт по умолчанию для удаленных соединений JMX?

Мой конкретный вопрос связан с JMX, который используется в JDK 1.6: если я запускаю процесс Java с использованием JRE 1.6 с

com.sun.management.jmxremote

в командной строке, использует ли Java порт по умолчанию для удаленных соединений JMX?

Backstory: В настоящее время я пытаюсь разработать процедуру предоставления клиенту, которая позволит им подключаться к одному из наших процессов через JMX с удаленной машины. Целью является упрощение их удаленной отладки ситуации, возникающей на консоли отображения в реальном времени. Из-за их соглашения об уровне обслуживания они сильно мотивированы для сбора как можно большего количества данных, и, если ситуация выглядит слишком сложной для исправления быстро, перезапустите консоль дисплея и позвольте ей подключиться к серверной стороне.

Мне известно, что я могу запустить jconsole для процессов JDK 1.6 и jvisualvm для процессов post-JDK 1.6.7 с физическим доступом к консоли. Однако из-за операционных требований и проблем с людьми мы настоятельно рекомендуем захватить данные, которые нам нужны удаленно, и запустить их снова.

РЕДАКТИРОВАТЬ: Мне известно свойство порта командной строки

com.sun.management.jmxremote.port=portNum

Вопрос, на который я пытаюсь ответить, - если вы не устанавливаете это свойство в командной строке, делает ли Java другой порт для удаленного мониторинга? Если да, то как вы могли бы определить, что это может быть?

4b9b3361

Ответ 1

Документация предполагает, что агент JMX использует локальный порт - что-то недостижимое извне машины - если вы не указали следующее свойство:

com.sun.management.jmxremote.port=portNum

Это по соображениям безопасности, а также по причине, данной г-ном Картофелем Голом. Таким образом, похоже, что Java 6 не открывает удаленный доступ по умолчанию для JMX.

EDIT: Добавлено после того, как OP добавила ответ с дополнительной информацией.

Другой вариант, который у вас есть, - это создать локальный прокси-сервер, который прослушивает все локальные соединения JMX и экспортирует эту информацию. Таким образом, вам не нужно иметь такую ​​магическую конфигурацию для каждого экземпляра JVM на сервере. Вместо этого локальный прокси-сервер может подключаться ко всем JVM через JMX, а затем как-то разоблачать эту информацию удаленно. Я не уверен, как вы это реализуете, но что-то вроде этого может быть меньше, чем то, что вам нужно было сделать, чтобы разоблачить все ваши JVM удаленно через JMX.

Ответ 2

AFAIK,

Ниже приведены возможности подключения клиентского процесса JMX (приложения управления, например jconsole, jmxterm, mc4j, jvmstat, jmxmonitor, jps,...) к процессу сервера JMX ( агент).

Предполагается, что протокол, соединяющий JMX-клиент и JMX-сервер, является "Java RMI" (он же "RMI-JRMP" ). Это должно быть значение по умолчанию. Можно настроить другие протоколы, в частности "RMI-IIOP" и "JMXMP". Возможны специальные протоколы: проект MX4J, например, предоставляет SOAP/HTTP и различные протоколы сериализации по протоколу HTTP.

Подробнее о конфигурации см. Sun/Oracle docs.

Также посмотрите файл jre/lib/management/management.properties в вашем дистрибутиве JDK.

Итак, возможности:

Случай 0: JVM запускается без какой-либо конкретной конфигурации

До Java 6: JVM не ведет себя как сервер JMX. Любая программа, которая запускается внутри JVM, может программно использовать JVM MBeanServer и использовать ее для проведения интересных обменов данными между потоками или для мониторинга JVM, но невозможно управление извне процесса JVM.

Так как Java 6: даже если это явно не настроено, можно получить доступ к JMX-функциям JVM локально (с той же машины), как описано в "Случай 1".

Случай 1: JVM запускается с -Dcom.sun.management.jmxremote

JVM настроен на работу как локальный (только для одного) JMX-сервер.

В этом случае (и в принципе только для Sun/Oracle JVM) клиент JMX может подключаться к серверу JMX через файлы с отображением памяти, найденные в /tmp/hsperfdata_[user]. Об этом говорится в документации Sun и называется "локальный мониторинг" (а также Attach API). Он не работает в файловых системах FAT, так как разрешения там не могут быть установлены правильно. См. эту запись в блоге.

Sun рекомендует запускать jconsole на машине, отдельно от сервера JMX, поскольку jconsole, по-видимому, является ресурсоемкой, поэтому этот "локальный мониторинг" не обязательно является хорошей идеей.

Локальный мониторинг, однако, довольно безопасен, он доступен только локально и легко управляется с разрешениями файловой системы.

Случай 2: Сервер JMX запускается с -Dcom.sun.management.jmxremote.port=[rmiregistryport]

JVM настроен на работу в качестве сервера JMX, прослушивающего несколько портов TCP.

Порт, указанный в командной строке, будет выделен JVM, и там будет доступен реестр RMI. В реестре размещается коннектор с именем "jmxrmi". Он указывает на второй, случайно распределенный TCP-порт ( "эфемерный" порт), на котором прослушивается сервер RMI JMX и через который происходит фактический обмен данными.

Локальный, как описано в "Случай 1", всегда включен в "Случай 2".

Сервер JMX по умолчанию прослушивает все интерфейсы, поэтому вы можете подключиться к нему (и управлять им), локально подключившись к 127.0.0.1:[rmiregistryport], а также дистанционно подключиться к [любому внешнему IP-адресу]: [некоторые порт] удаленно.

Это означает, что вы должны смотреть на последствия безопасности. Вы можете заставить JVM прослушивать 127.0.0.1:[rmiregistryport], только установив -Dcom.sun.management.jmxremote.local.only=true.

Очень жаль, что нельзя указать, где будет выделен эфемерный порт - он всегда выбирается случайным образом при запуске. Это может означать, что ваш брандмауэр должен стать швейцарским сыном проклятых! Тем не менее, обходные пути. В частности, Apache Tomcat устанавливает эфемерный порт сервера JMX RMI через его JMX Remote Lifecycle Listener. Код для выполнения этой маленькой магии можно найти на org.apache.catalina.mbeans.JmxRemoteLifecycleListener.

Если вы используете этот метод, вы также можете убедиться, что:

  • Клиент JMX должен аутентифицироваться на сервере JMX.
  • Обмен TCP между клиентом и сервером шифруется с помощью SSL

Как это делается, описано в документации Sun/Oracle

Другие подходы

Вы можете делать интересные перестановки, чтобы не использовать протокол RMI. В частности, вы можете добавить к вашему процессу механизм сервлетов (например, Jetty). Затем добавьте сервлеты, которые транслируют некоторый обмен через HTTP на внутреннем уровне в прямой доступ к JVM MBeanServer. Тогда вы были бы в "случае 0", но все же имели возможности управления, возможно, через интерфейс на основе HTML. JBoss JMX Console является примером этого.

Больше не по теме, вы можете напрямую использовать SNMP (что-то, чего я не пробовал) в соответствии с этот документ.

Показать и рассчитать время

И теперь настало время для некоторого кода, чтобы проиллюстрировать обмен JXM. Мы берем вдохновение из учебника Sunoracle.

Это работает в Unix. Мы используем JVM, который настроен как сервер JMX, используя:

-Dcom.sun.management.jmxremote.port=9001

Мы используем lsof для проверки того, какие порты TCP он держит открытым:

lsof -p <processid> -n | grep TCP

Нужно увидеть что-то вроде этого: порт реестра и эфемерный порт:

java    1068 user  127u  IPv6 125614246                 TCP *:36828 (LISTEN)
java    1068 user  130u  IPv6 125614248                 TCP *:9001  (LISTEN)

Мы используем tcpdump для проверки обмена пакетами между клиентом JMX и сервером JMX:

tcpdump -l -XX port 36828 or port 9001

В домашнем каталоге мы установили файл .java.policy, чтобы позволить удаленному подключению клиента:

grant {
    permission java.net.SocketPermission 
    "<JMX server IP address>:1024-65535", "connect,resolve";
};

И тогда мы можем запустить это и посмотреть, что произойдет:

package rmi;

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

import javax.management.remote.rmi.RMIConnection;
import javax.management.remote.rmi.RMIServer;

public class Rmi {

    public static void main(String args[]) throws Exception {
        // We need a Security Manager (not necessarily an RMISecurityManager)
        if (System.getSecurityManager() == null) {
            System.setSecurityManager(new SecurityManager());
        }
        //
        // Define a registry (this is just about building a local data structure)
        // 
        final int comSunManagementJmxRemotePort = 9001;
        Registry registry = LocateRegistry.getRegistry("<JMX server IP address>", comSunManagementJmxRemotePort);
        //
        // List registry entries. The client connects (using TCP) to the server on the
        // 'com.sun.management.jmxremote.port' and queries data to fill the local registry structure.
        // Among others, a definition for 'jmxrmi' is obtained.
        //
        System.out.print("Press enter to list registry entries");
        System.in.read();
        String[] names = registry.list();
        for (String name : names) {
            System.out.println("In the registry: " + name);
        }
        //
        // 'Looking up' the entry registered under 'jmxrmi' involves opening and tearing down
        // a TCP connection to the 'com.sun.management.jmxremote.port', as well as a TCP
        // connection to an ephemeral secondary port chosen at server startup.
        // The actual object locally obtained is a "javax.management.remote.rmi.RMIServerImpl_Stub"
        // indicating where the ephemeral port is.
        // "RMIServerImpl_Stub[UnicastRef [liveRef: [endpoint:[$IP:$EPHEMERAL_PORT](remote),objID:[-62fb4c1c:131a8c709f4:-7fff, -3335792051140327600]]]]"        
        //
        System.out.print("Press enter to get the 'jmxrmi' stub");
        System.in.read();
        RMIServer jmxrmiServer = (RMIServer)registry.lookup("jmxrmi");
        System.out.println(jmxrmiServer.toString());
        //
        // Now get a "RMI Connection" to the remote. This involves setting up and tearing
        // down a TCP connection to the ephemeral port. 
        //        
        System.out.print("Press enter to get the 'RMIConnection'");
        System.in.read();
        RMIConnection rcon = jmxrmiServer.newClient(null);
        //
        // Ask away. This involves setting up and tearing
        // down a TCP connection to the ephemeral port. 
        //
        System.out.print("Press enter to get the 'domains'");
        System.in.read();
        for (String domain : rcon.getDomains(null)) {
            System.out.println("Domain: " + domain);
        }
        //
        // Ok, that will do. For serious applications, we better use the higher-level JMX classes
        //
    }   
}

Ответ 3

На самом деле есть недокументированное свойство, которое можно использовать для принудительного создания JMX-соединений с дистанционным доступом на случайных номерах портов.

-Dcom.sun.management.jmxremote.authenticate="false" 
-Dcom.sun.management.jmxremote="true" 
-Dcom.sun.management.jmxremote.ssl="false" 
-Dcom.sun.management.jmxremote.port="0"
-Dcom.sun.management.jmxremote.local.only="false"

Последние два свойства имеют наибольшее значение.

Ответ 4

Документация указывает, что агент JMX использует локальный эфемерный порт , если не указать следующее: свойство:

com.sun.management.jmxremote.port=portNum

Порты по умолчанию исключаются из-за того, что в одной системе может быть много java-приложений, и если бы был порт по умолчанию, можно было бы управлять только одним приложением! Вышеупомянутое свойство конфигурации предоставляется для экспресс-цели удаленного управления.

Если вы должны настаивать на использовании эфемерного порта, тогда URL-адрес агента JMX должен быть доступен из JVM через следующее системное свойство (хотя это, скорее всего, локальный адрес):

com.sun.management.jmxremote.localConnectorAddress

Примечание. Я думаю, вы всегда можете открыть сокет на удаленном доступном адресе и запросах прокси на локальный сокет, но использование доступного параметра кажется гораздо более привлекательным!

Ответ 5

Итак, короткий ответ на мой вопрос - "нет".

Однако интересно узнать, почему. Посмотрите на вывод netstat из действительного локального соединения. Вот порты, которые я вижу, открываются в результате того, что jconsole делает локальное подключение к себе. Как вы можете видеть, порт 1650 является локальным портом, используемым для информации JMX:

Proto  Local Address          Foreign Address        State
TCP    Gandalf:1650           Gandalf:1652           ESTABLISHED
TCP    Gandalf:1650           Gandalf:1653           ESTABLISHED
TCP    Gandalf:1650           Gandalf:1654           ESTABLISHED
TCP    Gandalf:1650           Gandalf:1655           ESTABLISHED
TCP    Gandalf:1650           Gandalf:1656           ESTABLISHED
TCP    Gandalf:1652           Gandalf:1650           ESTABLISHED
TCP    Gandalf:1653           Gandalf:1650           ESTABLISHED
TCP    Gandalf:1654           Gandalf:1650           ESTABLISHED
TCP    Gandalf:1655           Gandalf:1650           ESTABLISHED
TCP    Gandalf:1656           Gandalf:1650           ESTABLISHED

Однако этого недостаточно, чтобы попытаться подключить jconsole к localhost:1650. К сожалению, все, что будет чистым, это сообщение "Connection failed: no such object in table".

Итак, вывод моей первоначальной истории заключается в том, что если мы будем способствовать удаленному мониторингу с помощью JMX для наших клиентов, нам действительно нужно идентифицировать уникальные отдельные порты удаленного доступа для различных процессов Java, которые запускаются в нашей системе, К счастью, все это требует разумного использования аргумента VM:

com.sun.management.jmxremote.port=portNum

где мы почти наверняка будем иметь последовательный предварительно заданный диапазон portNum, чтобы клиент мог выбрать правильное удаленное приложение, используя номер порта.

Ответ 6

Недавно я работал, чтобы выяснить, как включить удаленное управление JMX из Java-кода, не требуя запуска JVM со специальными наборами свойств. Решением, на котором я остановился, является запуск собственного частного реестра RMI - достаточно просто - и выставить службу JMX в этом реестре. Я создаю свой собственный MBeanServer, а затем создаю новый JMXConnectorServer. JMXConnectorServer создается с помощью вызова типа

connector = JXMConnectorServerFactory.newJMXConnectorServer(url, null, server);

Где сервер - MBeanServer, а url - экземпляр JMXServiceURL.

URL-адрес имеет форму "service: jmx: rmi:///jndi/rmi://localhost:/jmxrmi", где port является (локальным) номером порта частного реестра. "jmxrmi" - это стандартное имя службы для службы JMX.

После установки этого и начала подключения я обнаружил, что могу подключиться к нему из jconsole с помощью имени узла: port.

Это полностью удовлетворяет мои потребности; Мне будет интересно узнать, видит ли кто-то недостаток в этом подходе.

Ссылка: JMX Tutorial, Chap. 3

Ответ 7

Если вы запускаете приложение на сервере приложений Glassfish, просто запустите следующую команду asadmin, вам нужно будет перезапустить все запущенные серверы, чтобы изменения повлияли на них.

./asadmin enable-secure-admin

Существуют дополнительные конфигурации сервера Glassfish для дальнейшего обеспечения безопасности, более подробно см. Подключение удаленно к Glassfish через JMX.