Administration WildFly en ligne de commande

L’outil CLI permet d’effectuer toutes les tâches d’administration de WildFly en ligne de commande.

Certaines commandes sont trop longues pour être affichées sur une seule ligne ; dans ce cas, je les ai affichées sur plusieurs lignes, avec '\' comme signe de continuation.

Premiers pas

Connexion

Pour se connecter au serveur WildFly local, démarré sans port-offset :

~# $JBOSS_HOME/bin/jboss-cli.sh --connect

En réalité, cette commande connecte jboss-cli au serveur définit comme serveur par défaut dans $JBOSS_HOME/bin/jboss-cli.xml.

Pour se connecter à un autre serveur WildFly, avec un port-offset à 100 :

~# $JBOSS_HOME/bin/jboss-cli.sh --connect --controller=myserver:10090

Pour démarrer en mode non-interactif, et exécuter un script :

~# $JBOSS_HOME/bin/jboss-cli.sh --connect --file=myscript.cli

Vocabulaire

Avec CLI, on travaille avec des commandes, des ressources (ou noeuds), des attributs et opérations sur ces ressources.

Aide sur une commande

Pour obtenir de l’aide sur une commande, il faut taper la commande puis --help. Par exemple :

[host:9990 /] deploy --help

Attention, ça marche bien pour les commandes, mais pas les opérations.

Aide sur une opération

Pour avoir de l’aide sur une opération, il faut utiliser la commande read-operation, en précisant le noeud et l’opération.

[host:9990 /] read-operation --node=/core-service=vault add

Commandes en standalone (management)

Cette partie concerne la configuration des accès aux outils de gestions (jboss-cli, web console, APIs).

Accès local

L’accès en localhost avec jboss-cli peut se faire sans authentification. Si cette possibilité vous gène, vous pouvez la désactiver :

[host:9990 /] /subsystem=elytron/security-domain=ManagementDomain:list-remove(name=realms, index=1)
[host:9990 /] reload

Après ça, même en localhost, CLI demandera une authentification.

Dans les anciens WildFly, avant elytron :

[host:9990 /] /core-service=management/security-realm=ManagementRealm/authentication=local:remove
[host:9990 /] reload

Gestion des droits

Pour activer le système d’autorisations RBAC :

[host:9990 /] /core-service=management/access=authorization:write-attribute(name=provider,value=rbac)

Pour revenir au fonctionnement par défaut :

[host:9990 /] /core-service=management/access=authorization:write-attribute(name=provider,value=simple)

Dans ce fonctionnement, pour qu’un utilisateur ait le droit d’accéder aux outils de management, il faut lui associer un de rôles prédéfinis.

Il est possible d’associer un rôle à tous les utilisateurs.

[host:9990 /] /core-service=management/access=authorization/role-mapping=Monitor:add(include-all=true)

On peut aussi associer explicitement des utilisateurs à un rôle.

[host:9990 /] /core-service=management/access=authorization/role-mapping=Administrator/include=alexis       \
                  :add(type=USER, name=alexis)

Enfin, on peut associer des groupes d’utilisateurs à un rôle.

[host:9990 /] /core-service=management/access=authorization/role-mapping=Administrator/include=app-admin    \
                  :add(type=USER, name=app-admin)

Dans la configuration initiale, les groupes d’utilisateurs sont définis dans le fichier standalone/configuration/mgmt-groups.properties.

Authentification LDAP

La première étape est de connecter WildFly à l’annuaire LDAP.

[host:9990 /] /core-service=management/ldap-connection=LdapConnection                             \
                :add(url="ldap://localhost:1389",search-dn="cn=Root ",search-credential="rootpwd")

Pour la deuxième étape, on configure l’authentification LDAP.

[host:9990 /] /core-service=management/security-realm=LdapRealm:add()
[host:9990 /] /core-service=management/security-realm=LdapRealm/authentication=ldap:add(     \
                  base-dn="ou=admin,ou=people,dc=sewatech,dc=fr",                            \
                  username-attribute="cn",                                                   \
                  connection="LdapConnection")
[host:9990 /] /core-service=management/management-interface=http-interface                   \
                  :write-attribute(name=security-realm,value=LdapRealm)

Maintenant, la liste des utilisateurs ayant accès au management n’est plus dans le fichier properties, mais dans l’annuaire LDAP.

Autorisations LDAP

Si on veut affiner les autorisations, on passe en mode RBAC et on peut associer les rôles WildFly aux groupes du LDAP.

[host:9990 /] batch
[host:9990 /] /core-service=management/security-realm=LdapRealm/authorization=ldap:add(connection="LdapConnection")
[host:9990 /] /core-service=management/security-realm=LdapRealm/authorization=ldap/group-search=group-to-principal    \
                  :add(group-name=SIMPLE, group-name-attribute=cn,                                                    \
                       base-dn="ou=admin,ou=groups,dc=sewatech,dc=fr", recursive=true, search-by=DISTINGUISHED_NAME,  \
                       principal-attribute=uniqueMember)
[host:9990 /] run-batch

Ensuite, il faut établir une correspondance entre groupes et rôles.

[host:9990 /] /core-service=management/access=authorization/role-mapping=Administrator:add
[host:9990 /] /core-service=management/access=authorization/role-mapping=Administrator/include=app-admin              \
                  :add(type=GROUP, realm=LdapRealm)

Communications TLS

La première étape est de créer la pair de clés :

~# keytool -genkeypair -alias jboss -keypass jbosskey                                        \
                       -keystore jboss.ks -storepass jbosskey                                \
                       -keyalg RSA -dname "cn=localhost"

Ensuite, on créé le server-identity qui utilise cette clé :

[host:9990 /] /core-service=management/security-realm=ManagementRealm/server-identity=ssl    \
                  :add(alias=jboss,                                                          \
                       key-password=jbosskey,                                                \
                       keystore-password=jbosskey,                                           \
                       keystore-path=jboss.ks,                                               \
                       keystore-provider=JKS,                                                \
                       keystore-relative-to=jboss.server.config.dir)

Enfin, on ouvre l’accès sécurisé à l’interface de management :

[host:9990 /] /core-service=management/management-interface=http-interface                   \
                  :write-attribute(name=secure-socket-binding,                               \
                                   value=management-https)
Ces commandes ne sont plus utiles pour utiliser un certificat auto-signé. WildFly peut générer son propre keystore au premier accès TLS.

Commandes en standalone (profile)

Cette partie concerne la configuration des subsystems de WildFly.

Authentification (legacy)

Créer un security domain avec des fichiers properties (pratique pour le dev) :

[host:9990 /] batch
[host:9990 /] /subsystem=security/security-domain=sw-xxx:add(cache-type=default)
[host:9990 /] /subsystem=security/security-domain=sw-xxx/authentication=classic:add()
[host:9990 /] /subsystem=security/security-domain=sw-xxx/authentication=classic/login-module=UsersRoles       \
                  :add(code=UsersRoles, flag=required,                                                        \
                       module-options={"usersProperties"=>"${jboss.server.config.dir}/sw-users.properties",   \
                                       "rolesProperties"=>"${jboss.server.config.dir}/sw-roles.properties"})
[host:9990 /] run-batch

Les fichiers sw-users.properties et sw-roles.properties doivent être placés dans le répertoire standalone/configuration.

Créer un security domain avec un annuaire LDAP :

[host:9990 /] batch
[host:9990 /] /subsystem=security/security-domain=sw-xxx:add(cache-type=default)
[host:9990 /] /subsystem=security/security-domain=sw-xxx/authentication=classic:add()
[host:9990 /] /subsystem=security/security-domain=sw-xxx/authentication=classic/login-module=LdapExtended   \
                  :add(code=LdapExtended, flag=required,                                                    \
                       module-options={"java.naming.provider.url"=>"ldap://127.0.0.1:1338/",                \
                                       "bindDN"=>"cn=Sewatech", "bindCredential"=>"aa",                     \
                                       "baseCtxDN"=>"ou=people,dc=sewatech,dc=fr",                          \
                                       "baseFilter"=>"(uid={0})",                                           \
                                       "rolesCtxDN"=>"ou=groups,dc=sewatech,dc=fr",                         \
                                       "roleFilter"=>"(uniqueMember={1})", "roleAttributeID"=>"cn"})
[host:9990 /] run-batch

Créer un security domain en identité sécurisée (pour une utilisation depuis une DataSource) :

[host:9990 /] batch
[host:9990 /] /subsystem=security/security-domain=ds-xxx:add(cache-type=default)
[host:9990 /] /subsystem=security/security-domain=ds-xxx/authentication=classic:add()
[host:9990 /] /subsystem=security/security-domain=ds-xxx/authentication=classic/login-module=SecureIdentity        \
                  :add(code=SecureIdentity, flag=required,                                                         \
                       module-options={"principal"=>"sax","userName"=>;"sa","password"=>;"9fdd42c2a7390d3"})
[host:9990 /] run-batch

Le mot de passe chiffré est obtenu avec le script bash suivant :

~# export MODULES_HOME=$JBOSS_HOME/modules/system/layers/base
~# java -cp $MODULES_HOME/org/picketbox/main/*.jar org.picketbox.datasource.security.SecureIdentityLoginModule sa
Ce paragraphe est qualifié de legacy car le sous-système /subsystem=security a progressivement été remplacé par le sous-système /subsystem=elytron. Les deux ont cohabité entre WildFly 11 et 24, puis seul elytron a été conservé dans WildFly 25.

Communications TLS

La clé TLS peut être la même que pour le management, ou générée de la même façon.

Ensuite, on crée un connector sur le port 8443, qui utilise la clé (placée dans le répertoire configuration de mon WildFly).

[host:9990 /] /core-service=management/security-realm=ApplicationRealm/server-identity=ssl   \
                  :add(alias=server, key-password=password,                                  \
                       keystore-password=password, keystore-path=application.keystore,       \
                       keystore-provider=JKS, keystore-relative-to=jboss.server.config.dir)

[host:9990 /] /subsystem=undertow/server=default-server/https-listener=https                 \
                  :add(socket-binding=https, security-realm=ApplicationRealm)

Communications HTTP/2

Les communications HTTP/2 passent par un listener HTTPS.

[host:9990 /] /subsystem=undertow/server=default-server/https-listener=https                 \
                  :write-attribute(name=enable-http2, value=true)

Datasource

Il y a deux syntaxes possibles pour manipuler les datasources. Soit on applique des opérations sur le noeud correspondant à la datasource, dans le sous-système /subsystem=datasources, soit on utilise la commande datasource.

Créer une datasource, avec une nom d’utilisateur et un mot de passe :

[host:9990 /] data-source add --name=MyDS2 --jndi-name="java:jboss/datasources/MyDS2"        \
                              --connection-url="jdbc:mysql://localhost:3306/test"            \
                              --driver-name=h2 --user-name="sa" --password="sa"

ou

[host:9990 /] /subsystem=datasources/data-source=MyDS                                        \
                    :add(jndi-name="java:jboss/datasources/MyDS",                            \
                         connection-url="jdbc:mysql://localhost:3306/test",                  \
                         driver-name="mysql", user-name="sa", password="sa" )

Créer une datasource rattachée à un domaine de sécurité qui fournit le nom d’utilisateur et le mot de passe (cf. plus haut, identité sécurisée en mode legacy) :

[host:9990 /] data-source add --name=MyDS2 --jndi-name="java:jboss/datasources/MyDS2"        \
                --connection-url="jdbc:mysql://localhost:3306/test"                          \
                --driver-name=h2 --security-domain="ds-xxx"

La syntaxe avec l’opération :add, plutôt que la commande datasource add peut aussi être utilisée.

Connaitre la liste des datasources

[host:9990 /] ls /subsystem=datasources/data-source

Activer les statistiques d’une datasource :

[host:9990 /] /subsystem=datasources/data-source=MyDS                                        \
                    :write-attribute(name=statistics-enabled, value=true)

Lire l’état du pool de connexion d’une datasource :

[host:9990 /] /subsystem=datasources/data-source=MyDS/statistics=pool                        \
                    :read-resource(include-runtime=true)

Déploiement

Déployer une application :

[host:9990 /] deploy ~/to-bo-deployed/my-web-app.war

Retirer une application :

[host:9990 /] undeploy my-web-app.war

Messaging

Attention, pour que le messaging fonctionne, il faut avoir démarré WildFly en profil full ou ajouté le subsystem messaging à votre profil.

Ajouter une queue JMS :

[host:9990 /] /subsystem=messaging-activemq/server=default/jms-queue=SWq                     \
                  :add(entries=[java:/queue/SwQueue])

Ajouter une entrée JNDI à un connection factory :

[host:9990 /] /subsystem=messaging-activemq/server=default/connection-factory=RemoteConnectionFactory   \
                  :add-jndi(jndi-binding=java:jboss/exported/jms/ConnectionFactory)

Ajouter une entrée JNDI à une destination :

[host:9990 /] /subsystem=messaging-activemq/server=default/jms-queue=SWq                     \
                  :add-jndi(jndi-binding=java:jboss/exported/jms/SWq)

Désactiver la sécurité JMS :

[host:9990 /] /subsystem=messaging-activemq/server=default                                   \
                  :write-attribute(name=security-enabled, value=false)

Associer un domaine de sécurité à HornetQ :

[host:9990 /] /subsystem=messaging-activemq/server=default                                   \
                  :write-attribute(name=security-domain, value=hornetq)

Logging

Modifier le niveau de logs global :

[host:9990 /] /subsystem=logging/root-logger=ROOT:write-attribute(name=level, value=WARN)
Toutes les opérations spécifiques sur root-logger=ROOT ont été dépréciées. root-logger=ROOT doit maintenant être traité comme les autres loggers.

Ajouter un handler :

[host:9990 /] /subsystem=logging/periodic-rotating-file-handler=SWFILE                            \
                  :add(file={path=swmsgx.log, relative-to=jboss.server.log.dir},                  \
                       suffix=yyyy-MM-dd,                                                         \
                       formatter="%d %-5p [%c] (%t) %s%e%n")

Ajouter un logger :

[host:9990 /] /subsystem=logging/logger=fr.sewatech:add(level=TRACE)

Associer un logger à un handler :

[host:9990 /] /subsystem=logging/logger=fr.sewatech:add-handler(name=SWFILE)

Récupérer la liste des fichiers de log :

[host:9990 /] ls /subsystem=logging/log-file

Lire un fichier de log complet :

[host:9990 /] /subsystem=logging/log-file=server.log:read-log-file(lines=-1, skip=0)

Suivre un fichier de log (tail) :

[host:9990 /] /subsystem=logging/log-file=server.log:read-log-file(tail=true, lines=10, skip=0)

Désactiver la détection par le logging d’une configuration embarquée dans l’application :

[host:9990 /] /subsystem=logging:write-attribute(name=use-deployment-logging-config, value=false)

Désactiver les dépendances implicites vers les modules de logging :

[host:9990 /] /subsystem=logging:write-attribute(name=add-logging-api-dependencies,value=false)

Access Log

Ajouter les logs d’accès Web :

[host:9990 /] /subsystem=undertow/server=default-server/host=default-host/setting=access-log:add

On peut aussi modifier le pattern des logs, en faisant attention aux espaces et doubles-quotes :

[host:9990 /] cd /subsystem=undertow/server=default-server/host=default-host/setting=access-log
[host:9990 /] :write-attribute(name=pattern, value="%h %l %u %t \"%r\" %s %b %D")

Les logs d’accès ne sont écrits qu’à partir du prochain redémarrage (ou rechargement).

Développement (JSP)

En phase de développement, on a pris l’habitude avec Jetty ou Tomcat de pouvoir modifier à chaud les ressources Web. Ceci est désactivé par défaut dans WildFly et peut être réactivé avec la commande

[host:9990 /] /subsystem=undertow/servlet-container=default/setting=jsp:write-attribute(name=development, value=true)

Compression HTTP

Pour activer la compression des flux HTTP, il faut ajouter le filtre gzip et le configurer pour qu’il ne compresse que les flux de type texte.

[host:9990 /] /subsystem=undertow/configuration=filter/gzip=gz:add
[host:9990 /] /subsystem=undertow/server=default-server/host=default-host/filter-ref=gz                                      \
                   :add(predicate=                                                                                           \
                         "exists['%{o,Content-Type}']                                                                        \
                          and regex[pattern='(?:application/javascript|text/css|text/html|text/xml|application/json)(;.*)?', \
                                   value=%{o,Content-Type},                                                                  \
                                   full-match=true]")

Il faut aussi ajouter le header Vary pour informer les intermédiaires comme des reverse proxies.

[host:9990 /] /subsystem=undertow/configuration=filter/response-header=vary:add(header-name=Vary, \
                                               header-value=Accept-Encoding)
[host:9990 /] /subsystem=undertow/server=default-server/host=default-host/filter-ref=vary:add

Supprimer IIOP

Pour déployer une application qui utilise JMS, il faut le subsystem messaging-activemq, et pour ça le plus simple est de démarrer en profil full.

Malheureusement le profil full active aussi IIOP qui est rarement utile et qui ouvre un port supplémentaire. Le script suivant permet de supprimer IIOP du profil full.

[host:9990 /] /subsystem=ejb3/service=iiop:remove
[host:9990 /] /subsystem=iiop-openjdk:remove

[host:9990 /] /extension=org.wildfly.iiop-openjdk:remove

[host:9990 /] /socket-binding-group=standard-sockets/socket-binding=iiop:remove
[host:9990 /] /socket-binding-group=standard-sockets/socket-binding=iiop-ssl:remove
[host:9990 /] reload
[host:9990 /] /interface=unsecure:remove

Remoting sécurisé (legacy)

Il y a deux aspects à traiter pour sécuriser les connexions distantes aux EJB ou à JMS : l’authentification et la confidentialité du transport.

La validation des identités est traitée par un secuurity domain et l’échange des identités est traitée par un realm, par défaut ApplicationRealm.

Pour que l'authentification fonctionne correctement, il faut reconfigurer l'`ApplicationRealm` en remplaçant l’élement authentication par un <jaas>. Plutôt que de modifier ApplicationRealm, on peut aussi en créer un nouveau.

[host:9990 /] /core-service=management/security-realm=RemotingRealm:add
[host:9990 /] /core-service=management/security-realm=RemotingRealm/authentication=jaas:add(name=sw-domain)

Ensuite, il faut reconfigurer le subsystem remoting en modifiant l’http-connector existant ou en créant un nouveau :

[host:9990 /] /subsystem=remoting/http-connector=http-remoting-connector              \
                         :add(connector-ref=default, security-realm=RemotingRealm)

Par défaut, les accès remote se font avec le protocole remoting+http. Ce protocole passe par le port HTTP par défaut, avec un UPGRADE.

Pour sécuriser les communications, on peut basculer en remoting+https.

[host:9990 /] /subsystem=remoting/http-connector=http-remoting-connector:write-attribute(name=connector-ref, value=https)

Evidemment, cette sécurisation peut être mise en place dès la création du connector,

[host:9990 /] /subsystem=remoting/http-connector=http-remoting-connector:add(connector-ref=https, security-realm=RemotingRealm)

La référence sur le connector https implique qu’il y ait un https-listener de ce nom dans le subsystem undertow. C’est le cas par défaut depuis WildFly 10.1.

Commandes en domaine

Subsystems et profiles

La principale différence par rapport au fonctionnement en standalone, c’est que les subsystems ne sont plus directement à la racine, mais dans un profile.

Par exemple, pour une queue JMS, dans le profile full :

[host:9990 /] /profile=full/subsystem=messaging-activemq/server=default/jms-queue=SWq:add(entries=[java:/queue/SwQueue])

Serveurs

Démarrer un serveur (server-one) :

[host:9990 /] /host=local/server-config=server-one:start

Arrêter un serveur :

[host:9990 /] /host=local/server-config=server-one:stop

Applications

Déployer une application sur tous les serveurs du domaine :

[host:9990 /] deploy my-app.war --all-server-groups

Déployer une application sur certains groupes de serveurs :

[host:9990 /] deploy my-app.war --server-groups=server-group-one,server-group-two

Retirer une application de tous les serveurs du domaine :

[host:9990 /] undeploy my-app.war --all-relevant-server-groups

Retirer une application de certains groupes de serveurs :

[host:9990 /] undeploy my-app.war --server-groups=server-group-one --keep-content

Personnalisation

Il existe deux possibilités d’enrichir les commandes de jboss-cli, soit avec la commande alias, soit avec la commande command.

commande

Avec command, il est possible de créer de nouvelles commandes mappées avec des opérations sur des noeuds.

alias

Avec 'alias_, il est possible de créer donner des noms simples et expressif à des commandes complexes. Sa syntaxe est proche de l*alias*_* système.*

Par exemple, une fois l’alias deploy-app défini comme ci-dessous, il servira à déployer l’application myapplication.war dans le groupe server-group-one :

[host:9990 /] alias deploy-app="deploy ~/to-be-deployed/myapplication.war --server-groups=server-group-one"

Pour afficher un alias, on appelle :

[host:9990 /] alias deploy-app

Et l’appel de alias sans argument affiche la liste des alias.

Références