web-dev-qa-db-fra.com

Java 6 ouvre-t-il un port par défaut pour les connexions à distance JMX?

Ma question spécifique concerne JMX tel qu’il est utilisé dans JDK 1.6: si j’exécute un processus Java utilisant JRE 1.6 avec 

com.Sun.management.jmxremote

dans la ligne de commande, Java choisit-il un port par défaut pour les connexions JMX distantes?

Backstory: J'essaie actuellement de développer une procédure à donner à un client pour lui permettre de se connecter à l'un de nos processus via JMX à partir d'une machine distante. L'objectif est de faciliter le débogage à distance d'une situation se produisant sur une console d'affichage en temps réel. En raison de leur contrat de niveau de service, ils sont fortement motivés pour capturer le plus de données possible et, si la situation semble trop compliquée à résoudre rapidement, redémarrer la console d'affichage et lui permettre de se reconnecter côté serveur.

Je sais que je pourrais exécuter jconsole sur les processus JDK 1.6 et jvisualvm sur les processus postérieurs à JDK 1.6.7 étant donné l'accès physique à la console. Toutefois, en raison des exigences opérationnelles et des problèmes de personnel, nous sommes fermement motivés pour récupérer les données dont nous avons besoin à distance et les remettre en service.

EDIT: je suis au courant de la propriété du port de ligne de commande 

com.Sun.management.jmxremote.port=portNum

La question à laquelle j'essaie de répondre est la suivante: si vous ne définissez pas cette propriété sur la ligne de commande, Java choisit-il un autre port pour la surveillance à distance? Si oui, comment pourriez-vous déterminer ce que cela pourrait être?

28
Bob Cross

La documentation suggère que l'agent JMX utilise un port local, ce qui est inaccessible de l'extérieur de la machine, à moins que vous ne spécifiiez la propriété suivante:

com.Sun.management.jmxremote.port=portNum

C'est pour des raisons de sécurité, ainsi que pour la raison donnée par M. Potato Head. Ainsi, il semble que Java 6 n'ouvre pas un port accessible à distance par défaut pour JMX.

EDIT: ajouté après que l'OP ait ajouté une réponse avec plus d'informations.

Une autre option consiste à créer en quelque sorte un proxy local qui écoute toutes les connexions JMX locales et exporte ces informations. De cette façon, vous n'avez pas besoin d'une telle configuration magique de chaque instance JVM sur le serveur. Au lieu de cela, le proxy local peut se connecter à toutes les machines virtuelles via JMX, puis exposer ces informations à distance. Je ne suis pas certain de la manière dont vous allez implémenter cela, mais cela peut être moins fastidieux que ce que vous devez faire autrement pour exposer toutes vos machines virtuelles à distance via JMX.

38
Eddie

AFAIK,

Voici les possibilités pour connecter un processus client JMX (une application management comme jconsole, jmxterm, mc4j, jvmstat, jmxmonitor, jps, ...) à un processus serveur JMX (le agent).

Le protocole connectant le client JMX et le serveur JMX est supposé être «Java RMI» (ou «RMI-JRMP»). Cela devrait être le défaut. On peut configurer autres protocoles , en particulier 'RMI-IIOP' et 'JMXMP'. Des protocoles spéciaux sont possibles: le projet MX4J , par exemple, fournit en outre SOAP/HTTP et divers protocoles de sérialisation sur HTTP. 

Reportez-vous à docs Sun/Oracle pour plus de détails sur la configuration.

Consultez également le fichier jre/lib/management/management.properties dans votre distribution JDK.

Donc, les possibilités:

Cas 0: la JVM est démarrée sans configuration particulière

Avant Java 6: la machine virtuelle Java ne se comportait pas comme un serveur JMX. Tout programme exécuté dans la machine virtuelle Java peut accéder au programme MBeanServer de la machine virtuelle et l'utiliser pour effectuer des échanges de données intéressants entre les threads ou pour effectuer la surveillance de la machine virtuelle, mais aucune gestion en dehors du processus de la machine virtuelle n'est possible.

Depuis Java 6: Même sans configuration explicite, on peut accéder à la fonctionnalité JMX de la machine virtuelle localement (à partir du même ordinateur), comme décrit dans le "Cas 1".

Cas 1: La JVM est démarrée avec -Dcom.Sun.management.jmxremote 

La machine virtuelle Java est configurée pour fonctionner en tant que serveur JMX local (sur la même machine uniquement).

Dans ce cas (et en principe uniquement pour les machines JVM Sun/Oracle), un client JMX peut se connecter au serveur JMX via des fichiers mappés en mémoire situés dans /tmp/hsperfdata_[user]. Ceci est mentionné dans la documentation de Sun et appelé "surveillance locale" (ainsi que la Attach API ). Cela ne fonctionne pas sur les systèmes de fichiers FAT car les autorisations ne peuvent pas être définies correctement. Voir cette entrée de blog .

Sun recommande d'exécuter jconsole sur une machine distincte du serveur JMX, car jconsole est apparemment un gros problème de ressources. Cette opération de "surveillance locale" n'est donc pas forcément une bonne idée.

Cependant, la surveillance locale est plutôt sécurisée, elle ne peut être utilisée que localement et facilement contrôlée par le biais d'autorisations de système de fichiers.

Cas 2: le serveur JMX est démarré avec -Dcom.Sun.management.jmxremote.port=[rmiregistryport] 

La machine virtuelle Java est configurée pour fonctionner en tant que serveur JMX à l'écoute de plusieurs ports TCP.

Le port spécifié sur la ligne de commande sera alloué par la JVM et un registre RMI y sera disponible. Le registre annonce un connecteur nommé "jmxrmi". Il pointe vers un deuxième port TCP alloué de manière aléatoire (un port "éphémère") sur lequel le serveur RMI JMX écoute et à travers lequel l'échange de données a lieu.

Local tel que décrit dans «Cas 1» est toujours activé dans «Cas 2».

Le serveur JMX écoute toutes les interfaces par défaut. Vous pouvez donc vous y connecter (et le contrôler) en vous connectant localement à 127.0.0.1:[rmiregistryport] et en vous connectant à distance à [toute adresse IP extérieure]: [certains ports] à distance. . 

Cela implique que vous devez examiner les implications pour la sécurité. Vous pouvez faire écouter la machine virtuelle Java sur 127.0.0.1:[rmiregistryport] uniquement en définissant -Dcom.Sun.management.jmxremote.local.only=true.

Il est plutôt regrettable qu’on ne puisse pas spécifier où le port éphémère sera alloué - il est toujours choisi au hasard au démarrage. Cela signifie peut-être que votre pare-feu doit devenir le fromage suisse des damnés! Cependant, il existe solutions de contournement . Apache Tomcat définit notamment le port éphémère du serveur RMI JMX via son écouteur de cycle de vie distant JMX . Le code pour effectuer cette petite magie peut être trouvé à org.Apache.catalina.mbeans.JmxRemoteLifecycleListener }.

Si vous utilisez cette méthode, vous pouvez également vous assurer que:

  1. Le client JMX doit s'authentifier auprès du serveur JMX
  2. L'échange TCP entre le client et le serveur est crypté à l'aide de SSL

La procédure à suivre est décrite dans Documentation de Sun/Oracle }

Autres approches

Vous pouvez faire des permutations intéressantes pour éviter de devoir utiliser le protocole RMI. En particulier, vous pouvez ajouter un moteur de servlet (comme Jetty) à votre processus. Ajoutez ensuite des servlets qui traduisent en interne un échange basé sur HTTP en accès directs à la variable MBeanServer de la machine virtuelle. Vous seriez alors dans le 'cas 0' mais auriez toujours des capacités de gestion, éventuellement via une interface HTML. La console JBoss JMX en est un exemple.

Plus hors sujet, vous pouvez utiliser SNMP directement (quelque chose que je n’ai pas essayé) selon ce document .

Montrer et indiquer l'heure 

Et maintenant, il est temps que du code illustre un échange JXM. Nous nous inspirons de tutoriel Sunoracle .

Cela fonctionne sous Unix. Nous utilisons une machine virtuelle configurée en tant que serveur JMX en utilisant:

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

Nous utilisons lsof pour vérifier quels ports TCP il maintient ouverts:

lsof -p <processid> -n | grep TCP

On devrait voir quelque chose comme ceci, le port de registre et le port éphémère:

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

Nous utilisons tcpdump pour inspecter l’échange de paquets entre le client JMX et le serveur JMX:

tcpdump -l -XX port 36828 or port 9001

Nous avons créé un fichier .Java.policy dans le répertoire de base pour permettre au client de se connecter à distance:

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

Et ensuite, nous pouvons exécuter ceci et voir ce qui se passe:

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
        //
    }   
}
87
David Tonhofer

En réalité, il existe une propriété non documentée que vous pouvez utiliser pour forcer JMX à créer des connecteurs accessibles à distance sur des numéros de port aléatoires.

-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"

Les deux dernières propriétés sont de la plus haute importance.

6
Ivan Koblik

La documentation semble indiquer que l'agent JMX utilise un port éphémère local, sauf si vous spécifiez la propriété suivante:

com.Sun.management.jmxremote.port=portNum

Les ports par défaut sont évités car vous pourriez avoir plusieurs applications Java sur un système, et s'il existait un port par défaut, seule une application pourrait être gérée! La propriété de configuration ci-dessus est fournie dans le but express de distant management.

Si vous devez insister sur l'utilisation d'un port éphémère, l'URL de l'agent JMX doit être accessible depuis la machine virtuelle Java, via la propriété système suivante (bien qu'il s'agisse probablement d'une adresse locale):

com.Sun.management.jmxremote.localConnectorAddress

Note: J'imagine que vous pouvez toujours ouvrir un socket sur une adresse accessible à distance et des requêtes de proxy sur le socket local, mais utiliser l'option disponible semble bien plus attrayant!

3
David Grant

Donc, la réponse courte à ma question est "non".

Cependant, il est intéressant d'examiner pourquoi. Regardez la sortie netstat d'une connexion locale valide. Voici les ports que je vois s'ouvrir à la suite d'une jconsole établissant une connexion locale avec lui-même. Comme vous pouvez le constater, le port 1650 est le port local utilisé pour les informations 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

Cependant, il n'est pas suffisant d'essayer de connecter jconsole à localhost:1650. Malheureusement, tout ce qui vous intéressera sera un message "La connexion a échoué: aucun objet de ce type dans la table".

La conclusion de mon récit initial est donc que, si nous voulons faciliter la surveillance à distance à l'aide de JMX pour nos clients, nous devons vraiment identifier des ports d'accès à distance individuels uniques pour les divers processus Java démarrés dans notre système. Heureusement, tout cela nécessite une utilisation judicieuse de l'argument VM:

com.Sun.management.jmxremote.port=portNum

où nous aurons presque certainement une plage séquentielle pré-spécifiée de portNum afin que le client puisse sélectionner l’application distante appropriée à l’aide du numéro de port.

2
Bob Cross

Je me suis récemment mis au travail pour savoir comment activer la gestion JMX à distance à partir de code Java, sans exiger que la machine virtuelle Java soit démarrée avec un ensemble de propriétés spéciales. La solution que j'ai choisie consiste à démarrer mon propre registre privé RMI - assez facilement - et à exposer le service JMX sur ce registre. Je crée mon propre MBeanServer, puis je crée un nouveau JMXConnectorServer. Le JMXConnectorServer est créé par un appel comme

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

Où serveur est le MBeanServer, et url est une instance de JMXServiceURL.

L'URL est de la forme "service: jmx: rmi: /// jndi/rmi: // localhost:/jmxrmi" où port est le numéro de port du registre privé (local). "jmxrmi" est le nom de service standard du service JMX.

Après avoir configuré et démarré le connecteur, je constate que je peux me connecter à partir de jconsole en utilisant le nom d’hôte: port.

Cela répond entièrement à mes besoins. Je serais intéressé de savoir si quelqu'un voit une faille dans cette approche.

Référence: Tutoriel JMX, Chap. 3

2
Sane Dog

Si vous exécutez votre application sur le serveur d'applications Glassfish, exécutez simplement la commande asadmin suivante. Vous devrez redémarrer tous les serveurs en cours d'exécution pour que les modifications prennent effet. 

./asadmin enable-secure-admin

Il existe d'autres configurations de serveur Glassfish pour renforcer la sécurité. Pour plus d'informations, reportez-vous à la section Connexion à distance à Glassfish via JMX .

0
Yu Chen