Administration WildFly en ligne de commande

Un article de JTips.
Administration WildFly en ligne de commande
Auteur : Alexis Hassler
Formation(s) sur le sujet :
Administration WildFly
Sewatech.png


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

Attention : 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é dans 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 :

$JBOSS_HOME/bin/jboss-cli.sh --connect --controller=myserver:10099

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 :

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.

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 :

/core-service=management/security-realm=ManagementRealm/authentication=local:remove
reload

Après ça, même en localhost, jboss-cli demandera une authentification.

Gestion des droits

Pour activer le système d'autorisations RBAC :

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

Pour revenir au fonctionnement par défaut :

/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.

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

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

/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.

/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.

/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.

/core-service=management/security-realm=LdapRealm:add()
/core-service=management/security-realm=LdapRealm/authentication=ldap:add(                       \
           base-dn="ou=admin,ou=people,dc=sewatech,dc=fr",                                       \
           username-attribute="cn",                                                              \
           connection="LdapConnection")
/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.

batch
/core-service=management/security-realm=LdapRealm/authorization=ldap:add(connection="LdapConnection")
/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)
run-batch

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

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

Communications SSL

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

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é :

/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 :

/core-service=management/management-interface=http-interface:write-attribute
                     (name=secure-socket-binding, 
                      value=management-https)

Commandes en standalone (profile)

Cette partie concerne la configuration des subsystems de WildFly.

Authentification

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

batch 
/subsystem=security/security-domain=sw-xxx:add(cache-type=default)
/subsystem=security/security-domain=sw-xxx/authentication=classic:add()
/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"})
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 :

batch 
/subsystem=security/security-domain=sw-xxx:add(cache-type=default)
/subsystem=security/security-domain=sw-xxx/authentication=classic:add()
/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"})
run-batch

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

batch
/subsystem=security/security-domain=ds-xxx:add(cache-type=default)  
/subsystem=security/security-domain=ds-xxx/authentication=classic:add()
/subsystem=security/security-domain=ds-xxx/authentication=classic/login-module=SecureIdentity    \
    :add(code=SecureIdentity, flag=required,                                                     \
         module-options={"principal"=>"sax","userName"=>"sa","password"=>"9fdd42c2a7390d3"})
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

Communications SSL

La clé SSL 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).

/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)

/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.

/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 :

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

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

Dans les deux cas, la datasource est créée, mais pas forcément encore activée (ça dépend des versions). Pour qu'elle soit vraiment utilisable :

/subsystem=datasources/data-source=MyDS:enable

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) :

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

ls /subsystem=datasources/data-source

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

cd /subsystem=datasources/data-source=MyDS/statistics=pool:read-resource(include-runtime=true)

Déploiement

Déployer une application :

deploy ~/to-bo-deployed/my-web-app.war

Retirer une application :

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 :

/subsystem=messaging/hornetq-server=default/jms-queue=SwQueue:add(entries=[java:/queue/SwQueue])

Ajouter une entrée JNDI à un connection factory :

/subsystem=messaging/hornetq-server=default/connection-factory=RemoteConnectionFactory:add-jndi(jndi-binding=java:jboss/exported/jms/ConnectionFactory)

Ajouter une entrée JNDI à une destination :

/subsystem=messaging/hornetq-server=default/jms-queue=SwQueue:add-jndi(jndi-binding=java:jboss/exported/jms/SWq)

Désactiver la sécurité JMS :

/subsystem=messaging/hornetq-server=default:write-attribute(name=security-enabled, value=false)

Associer un domaine de sécurité à HornetQ :

/subsystem=messaging/hornetq-server=default:write-attribute(name=security-domain, value=hornetq)

Logging

Modifier le niveau de logs global (à chaud) :

/subsystem=logging/root-logger=ROOT:change-root-log-level(level=WARN)

Ajouter un handler :

/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 :

/subsystem=logging/logger=fr.sewatech:add(level=TRACE)

Associer un logger à un handler :

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

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

/subsystem=logging/:list-log-files

Lire un fichier de log complet :

/subsystem=logging/:read-log-file(name=server.log,lines=-1,skip=0)

Suivre un fichier de log (tail) :

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

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

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

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

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

Access Log

Ajouter les logs d'accès Web :

/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 :

cd /subsystem=undertow/server=default-server/host=default-host/setting=access-log
: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

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

Headers HTTP de serveur

Par défaut, WildFly ajoute des headers qui indiquent que le serveur est du WildFly / Undertow, ce qui est parfaitement inutile.

/subsystem=undertow/server=default-server/host=default-host/filter-ref=server-header:remove
/subsystem=undertow/server=default-server/host=default-host/filter-ref=x-powered-by-header:remove

/subsystem=undertow/configuration=filter/response-header=server-header:remove
/subsystem=undertow/configuration=filter/response-header=x-powered-by-header:remove

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.

/subsystem=undertow/configuration=filter/gzip=gz:add
/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.

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

Remoting sécurisé

Il y a deux aspects à traiter pour sécuriser les connexions distantes aux jeB 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.

/core-service=management/security-realm=RemotingRealm:add
/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 :

/subsystem=remoting/http-connector=remoting-connector:add(connector-ref=default, security-realm=RemotingRealm)

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

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

/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,

/subsystem=remoting/http-connector=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 dans WildFly 10.1.

JBoss AS 7 / EAP 6

La plupart des commandes sont identiques entre JBoss et WildFly. Il y a quand même quelques commandes qui sont différentes.

Access Log

Pour JBoss AS 7, le subsystem undertow n'existait pas, son équivalent était le subsytem web, et la configuration était légèrement différente :

/subsystem=web/virtual-server=default-host/access-log=configuration:add

cd /subsystem=web/virtual-server=default-host/access-log=configuration
:write-attribute(name=pattern, value=%h\ %l\ %u\ %t\ "%r"\ %s\ %b\ %D)

Dans JBoss EAP 6, on a aussi le subsystem web, mais la commande est légèrement différente :

/subsystem=web/virtual-server=default-host/configuration=access-log:add

cd /subsystem=web/virtual-server=default-host/configuration=access-log
:write-attribute(name=pattern, value="%h %l %u %t \"%r\" %s %b %D")


Authentification

J'ai mis un peu de temps pour comprendre comment construire cette commande pour JBoss AS 7, mais la voilà, pour un security domain qui utilise une base de données.

/subsystem=security/security-domain=sw-domain:add(cache-type=default)
/subsystem=security/security-domain=sw-domain/authentication=classic                                         \
   :add(login-modules=[                                                                                      \
           {"code"=>"Database", "flag"=>"required",                                                          \
            "module-options"=>{"dsJndiName" => "java:/datasources/SewaDS",                                   \
                               "principalsQuery" => "select passwd from Users username where username=?",    \
                               "rolesQuery" => "select userRoles, 'Roles' from UserRoles where username=?"}}])

Et pour un security domain qui utilise des fichiers propoerties, ça ressemble beaucoup.

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

La configuration de JBoss EAP 6 est similaire à WildFly.

Développement

Dans JBoss AS 7, la commande était légèrement différente, puisqu'on avait un sous-système web à la place de undertow :

/subsystem=web/configuration=jsp-configuration:write-attribute(name=development, value=true)

Communications SSL

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

/subsystem=web/connector=https:add           \
                     (protocol=HTTP/1.1,     \
                      scheme=https,          \
                      secure=true,           \
                      socket-binding=https)
/subsystem=web/connector=https/ssl=configuration:add                            \
                     (certificate-key-file=${jboss.server.config.dir}/sewa.ks,  \
                      key-alias=sewa,                                           \
                      password=sewakey,                                         \
                      protocol=TLS)

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 :

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

Serveurs

Démarrer un serveur (server-one) :

/host=local/server-config=server-one:start

Arrêter un serveur :

/host=local/server-config=server-one:stop

Applications

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

deploy my-app.war --all-server-groups

Déployer une application sur certains groupes de serveurs :

deploy my-app.war --server-groups=server-group-one,server-group-two

Retirer une application de tous les serveurs du domaine :

undeploy my-app.war --all-relevant-server-groups

Retirer une application de certains groupes de serveurs :

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 lalias 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 :

alias deploy-app="deploy ~/to-be-deployed/myapplication.war --server-groups=server-group-one"

Pour afficher un alias, on appelle :

alias deploy-app

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

Attention : les alias ne sont pas persistants, ils disparaissent lorsqu'on redémarre jboss-cli. La persistance devrait être ajoutée à la version 10.0.0.Final.

Références

Quelques autres articles sur WildFly
Quelques autres articles sur JBoss