30 janvier 2009

Développement : Dependency Analyzer

Oui, pardon, il y a un outil bien connu qui permet de voir des dépendances d'un projet Maven : Dependency Analyzer, de jfrog, ceux qui font Artifactory.



C'est bien et utile, mais ce n'était pas mon besoin. Mon besoin était un besoin de m'y retrouver dans mon développement, pas de nettoyer ma configuration.

Dependency Analyzer :
  • me montre les dépendances extraites récursivement à partir d'un projet source donné,
  • donne toutes les bibliothèques, donc en particulier pléthore de bibliothèques tierces,
  • traite correctement les versions,
  • s'appuie sur les POMs installés du local repository.
Mon outil :
  • me montre les dépendances dans un espace de travail vu comme un ensemble,
  • est centré sur les projets dont j'ai les sources de l'espace de travail plutôt que les bibliothèques tierces ou les projets déjà construits,
  • fait n'importe quoi avec les versions (bug secondaire vu mon besoin),
  • s'appuie sur les POMs en cours de développement de l'espace de travail.
Les deux types d'outils se complètent.

Développement : visualisation de dépendances Maven

Voici une micro-documentation sur l'outil dont je parlais dans mes précédents billets, qui produit un diagramme UML à partir d'une analyse des dépendances entre des projets Maven.

Pour télécharger l'outil

En utilisant Maven, avec l'adresse de mon repository :
Ou en prenant directement le JAR : JAR with dependencies.

Pour utiliser l'outil depuis Ant

Pour ne pas trop se prendre la tête, mettre le JAR téléchargé avec les autres bibliothèques de Ant : répertoire $ANT_HOME/lib/. Sinon, il faut renseigner <classpath> dans les <taskdef> ci-dessous.

Le principe ensuite consiste à enchaîner deux tâches :
  1. concaténation de tous les POMs dans un seul fichier XML (xmlconcatscan)
  2. analyse de ce fichier et génération d'un diagramme UML (poms2uml)
Voici un exemple :
<project name="umlgen" default="umlgen">

<taskdef name="xmlconcatscan"
classname="com.avcompris.tools.ant.XMLConcatScanTask">
<!-- <classpath> ... </classpath> -->
</taskdef>

<taskdef name="poms2uml"
classname="com.avcompris.all.uml.ant.Poms2UmlTask">
<!-- <classpath> ... </classpath> -->
</taskdef>

<target name="umgen">

<mkdir dir="target" />

<xmlconcatscan destFile="temp.xml">
<fileset dir="/home/blabla/workspace"> <!-- workspace location -->
<include name="*/pom.xml" />
</fileset>
</xmlconcatscan>
<poms2uml srcFile="temp.xml"
destFile="all-poms.uml" />

</target>

</project>
Ce projet Ant scanne le répertoire « /home/blabla/workspace », puis produit un diagramme UML dans le fichier « all-poms.uml ». Ce fichier peut ensuite être ouvert et manipulé avec l'outil StarUML.


Diagramme après nettoyage dans StarUML

Cet exemple est fourni, plus complet, dans le projet Maven avc-commons-all-umldep.

Pour poser une question au support ;-)

Écrire un commentaire dans cet article de blog http://snurl.com/azp1d

Ou par e-mail : david.andriana@free.fr

Pour récupérer les sources en local

Note : les sources sont en UTF-8.

Elles se récupèrent en lecture sur http://svn.avcompris.com/

Faire un checkout, projet par projet :
svn co http://svn.avcompris.com/avc-commons-all-noci/trunk avc-commons-all-noci
svn co http://svn.avcompris.com/avc-commons-all-umldep/trunk avc-commons-all-umldep
svn co http://svn.avcompris.com/avc-commons-databeans/trunk avc-commons-databeans
svn co http://svn.avcompris.com/avc-commons-lang/trunk avc-commons-lang
svn co http://svn.avcompris.com/avc-commons-parent/trunk avc-commons-parent
svn co http://svn.avcompris.com/avc-commons-testutil/trunk avc-commons-testutil
svn co http://svn.avcompris.com/avc-plugin-common/trunk avc-plugin-common
svn co http://svn.avcompris.com/avc-plugin-parent/trunk avc-plugin-parent
svn co http://svn.avcompris.com/avc-tools-ant/trunk avc-tools-ant
svn co http://svn.avcompris.com/avc-tools-common/trunk avc-tools-common
svn co http://svn.avcompris.com/avc-tools-databeans-plugin/trunk avc-tools-databeans-plugin
svn co http://svn.avcompris.com/avc-tools-databeans-plugin-ut/trunk avc-tools-databeans-plugin-ut

(12 projets au total)
Ou alors une astuce sous Unix :
  1. créer des répertoires vides « avc-commons-all-noci », « avc-commons-all-umldep », etc. puis taper la commande suivante :
  2. for i in `ls`; do svn co http://svn.avcompris.com/$i/trunk $i; done
Pour construire tous les projets avec Maven en ligne de commande, se placer dans le répertoire « avc-commons-all-noci », et taper la commande : mvn install

Pour installer les sources dans son IDE

Pour Eclipse, une fois que tous les projets Maven ont été récupérés en local, se placer dans « avc-commons-all-noci », et taper la commande : mvn eclipse:eclipse

Cela crée les fichiers .project et .classpath attendus par Eclipse.

On peut ensuite importer les projets dans Eclipse.

Pour IDEA : File, Open Project..., et ouvrir avc-commons-all-noci/pom.xml.

Pour lancer l'outil depuis Eclipse

La classe de tests JUnit WorkspaceInsiemeUmlTest dans le projet avc-commons-all-umldep fait justement un test de scan, concaténation et génération d'UML. Il suffit donc de lancer le test JUnit, que ce soit depuis son IDE ou en ligne de commande. Exemple : mvn test

Une fois le test lancé on peut récupérer le fichier « all-poms.uml » dans le répertoire « target », et c'est le même que ci-dessus.

Développement : packaging de l'outil de visualisation de dépendances Maven

Voici que quelqu'un qui lit mon blog me demande l'outil qui produit un diagramme UML correspondant aux dépendances entre mes projets Maven... Voir mon post ici : Visualiser les dépendances entre projets Maven.

OK, certes c'est à sa propre demande et à ses risques et périls, et il est par ailleurs simple d'envoyer le JAR par e-mail à cette personne.

Mais alors quid de :
  1. la capitalisation ? C'est-à-dire, cette démarche d'envoyer le JAR, comment la répéter à moindre coût si on me refait plus tard la demande ?
  2. la documentation ? Je n'ai pas tellement envie de passer du temps là-dessus.
  3. le support ?
  4. ... la capitalisation du support : il y aura des bugs, des questions, auxquelles je répondrai volontiers, mais comment capitaliser là-dessus ?
  5. ... l'accès aux entrailles de l'outil : le mieux serait de donner accès aux sources.
  6. la licence sur l'outil et son utilisation ? - voire sur ses sources.
Donc, cher fred que je ne connais pas ;-) je propose que nous fassions simple :
  • pour le JAR, je le rends publiquement disponible sur mon serveur Nexus. Le téléchargement se fait donc par en utilisant comme repository Maven : http://ref.avcompris.com/nexus/content/repositories/public-releases/ Ou directement pour le JAR en question : JAR with dependencies.
  • pour les sources sous Subversion, je les mets ici, accessibles en lecture seule par HTTP sans authentification : http://svn.avcompris.com/
  • pour la documentation, eh bien je vais faire un article dans ce blog pour expliquer succinctement comment faire fonctionner l'outil. Prochainement. Le reste proviendra des réponses aux questions.
  • pour le support, donc, envoyer questions/réponses dans le blog ou par e-mail.
  • pour la licence :
  • ... utilisation et modification qui imposent de respecter et de mentionner le nom de l'auteur David Andrianavalontsalama, ou encore David Andriana an nom d'usage
  • ... application de la licence open source BSD, dont une version en anglais est ici : The BSD License
  • ... utilisation et modification libres sans restriction à part celles qui précèdent.
  • logiciel et sources mis à disposition sans aucune garantie de quoi que ce soit et attention ce logiciel peut partir en vrille et vous êtes responsable de ce qui arrive, à vous ou à d'autres, à partir du moment où vous l'utilisez.
Je ne vais pas recopier tout de suite la licence dans les fichiers sources. Manque de temps, tout ça.

Déjà qu'il me faut faire les deux trois choses qui suivent...

...

Plus de quatre heures après (eh oui !), voici ce que ça m'a coûté :
  • créer sur mon serveur Nexus un repository ouvert au public pour les 12 projets Maven concernés,
  • changer la configuration <distributionManagement> des projets Maven pour qu'ils se déploient désormais dans ce repository,
  • passer deux projets de mon repository Subversion privé vers mon repository public,
  • rajouter une ligne de Copyright dans les fichiers,
  • faire une petite revue de javadoc rapide « histoire de »,
  • passer quelques tests maison orientés qualité,
  • reconstruire les projets,
  • lancer les releases Maven,
  • rédiger et publier des articles de blog.
Je suis à quelques jours d'une mise en production d'un projet qui dépend de ceux-là : il était donc de toute façon nécessaire que je fasse tout ce qui concerne les releases Maven. On va dire que le fait de rédiger de la doc est un effet collatéral positif ;-)

Voilà fred, tu pourras me faire ton retour...

27 janvier 2009

Développement : à propos d'injection de dépendances (DI)

Petit topo sur la DI telle que je la pratique en Java en ce moment.

Généralités

L'injection de dépendances (en anglais Dependency Injection, DI) est aujourd'hui un principe fondamental de la programmation. Elle évite par exemple de devoir instancier directement des implémentations. Du code comme celui-ci :
public class PeopleManagerImpl implements PeopleManager {

    private final EmailSender sender = new EmailSenderImpl();
    private final Collection<Person> people = 
        new ArrayList<Person>();

    public void welcomeNewPerson(String name, String email) {
        people.add(new Person(name, email));
        sender.sendMail(mail, "Welcome " + name);
    }
}
Devient par exemple avec l'injection de dépendances :
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.inject.Inject;

public class PeopleManagerImpl implements PeopleManager {

    private final EmailSender sender;
    private final Collection<Person> people = 
        new ArrayList<Person>();

    @Inject
    public PeopleManagerImpl(EmailSender sender) {
        this.sender = checkNotNull(sender);
    }

    public void welcomeNewPerson(String name, String email) {
        people.add(new Person(name, email));
        sender.sendMail(mail, "Welcome " + name);
    }
}
L'injection de dépendances est particulièrement utile pour les tests, ainsi que pour la programmation orientée aspects (AOP).

Comparée à d'autres types d'injection de champs, par exemple via des accesseurs ou en manipulant directement les champs, je trouve que l'injection par le constructeur est la plus fiable. Vu qu'on peut tout manipuler au runtime en Java cela relève au final d'une sorte de convention, mais cette convention est bien relayée par les compilateurs et les IDEs.

Comme on l'aura compris au vu du code précédent, c'est l'approche suivie par Google Guice, bibliothèque que j'apprécie particulièrement. Parmi ses avantages :
  • son API est typée (Java 5),
  • la responsabilité de la bibliothèque est bel et bien limitée à l'injection de dépendances,
  • la configuration se fait en Java, donc avec les avantages du typage, et non en XML ou autre.
Pour le code ci-dessus, voici un exemple de code client qui récupère une instance et l'utilise :
PeopleManager peopleManager =    
    injector.getInstance(PeopleManager.class);
peopleManager.welcomeNewPerson(
    "Jean Dupont", "jean.dupont@xxx.com");
Injecter une ressource avec cycle de vie

Parlons par exemple de l'injection d'une connexion SQL, qu'il s'agit de fermer à la fin de son utilisation. Je sais je sais, je suis vieux jeu en gérant mon mapping O/R moi-même.

Le code suivant accède à la base de données :
public class PeopleManagerImpl implements PeopleManager {

    private final Collection<Person> people = 
        new ArrayList<Person>();

    public void persistPeople() {
        try {
            Connection cxn = ...; // ?
            try {
                PreparedStatement pstmt = 
                  cxn.prepareStatement("INSERT INTO people"
                    + " (name, email) VALUES (?, ?)");
                try {
                    for (Person p : people) {
                        pstmt.setString(1, p.getName());
                        pstmt.setString(2, p.getName());
                        pstmt.executeUpdate();
                    }
                } finally {
                    pstmt.close();
                }
            } finally {
                cxn.close();
            }
        } catch (SQLException e) {
            throw new UncheckedBusinessException(e);
        }
    }
}


Noter la transformation des SQLException en exceptions métier (qui soient Runtime, histoire d'alléger le code).

La question est : comment récupérer l'objet Connection ? On ne peut pas l'injecter par le constructeur de PeopleManagerImpl, car chaque appel de méthode qui accède à la base de données se termine par un cxn.close() donc au bout du deuxième appel sur le même objet cxn ça plantera.

La solution est évidemment d'injecter une factory, en l'occurrence un provider qui crée une nouvelle  connexion à chaque fois :
public class PeopleManagerImpl implements PeopleManager {

    private final ConnectionProvider cxnProvider;
    ...

    @Inject
    public PeopleManagerImpl(ConnectionProvider cxnProvider) {
        this.cxnProvider = checkNotNull(cxnProvider);
    }

    public void persistPeople() {
        try {
            Connection cxn = cxnProvider.createConnection();
            try {
                PreparedStatement pstmt = 
                    cxn.prepareStatement(...);
                ...
            } finally {
                cxn.close();
            }
        } catch (SQLException e) {
            throw new UncheckedBusinessException(e);
        }
    }
}
Personnellement je trouve super pénible cette obligation d'appeler soi-même la méthode create() sur la factory et de faire le close() final (eh oui, il n'y a pas de destructeurs en Java), sans compter la transformation de l'exception. 9 lignes de code pour rien au total.

Injection de paramètres dans les méthodes

J'ai donc trouvé une autre solution : changer la signature des méthodes dans l'implémentation,  pour y injecter des objets dont le cycle de vie est géré par le container.

Cela implique de casser le lien entre interface et implémentation. Même si je déclare ce lien à la compilation par une interface paramétrée - que j'ai appelée BeanImpl -, la cohérence entre les méthodes de l'implémentation et celles de l'interface ne sont vérifiées qu'au runtime.

Les méthodes à appeler dans le cycle de vie sont également annotées. Quant à la classe d'implémentation elle-même, elle continue d'être gérée par Guice, ce qui permet d'utiliser @Inject.
public class PeopleManagerImpl 
implements BeanImpl<PeopleManager> {

     final ConnectionProvider cxnProvider;

     @Inject
     public PeopleManagerImpl(ConnectionProvider cxnProvider) {
          this.cxnProvider = checkNotNull(cxnProvider);
     }

     public void persistPeople(@BeanInject Connection cxn) 
     throws SQLException {
            PreparedStatement pstmt = cxn.prepareStatement(...);
            try {
                ...
            } finally {
                pstmt.close();
            }
     }

     @InitBeanInject
     private Connection createConnection() 
     throws SQLException {
          return cxnProvider.createConnection();
     }

     @ReleaseBeanInject
     private void releaseConnection(Connection cxn) 
     throws SQLException {
          cxn.close();
     }
}
Je gagne ainsi en lisibilité dans mes méthodes (sauf quand le code est affiché dans Blogger, apparemment...), donc en productivité de développement.


Noter que le code du client est toujours le suivant :
PeopleManager peopleManager = 
    injector.getInstance(PeopleManager.class);
...
À la compilation, il n'y a plus qu'un lien lâche entre l'interface et l'implémentation-cœur. Une autre contrainte de cette méthode est que l'interface du Bean PeopleManager ne peut pas avoir deux méthodes de même nom avec des signatures différentes. En fait cela était déjà une convention dans mes programmes, pour des raisons de portabilité vers d'autres langages comme PHP.

En tout cas j'aime bien.

26 janvier 2009

Développement : visualiser les dépendances entre projets Maven

Mon projet global est constitué d'une bonne centaine de projets Maven. Cette structure est assez pratique pour l'intégration continue (voir le tableau de bord Continuum 1.0.3) et pour vérifier que tout fonctionne sur différentes plates-formes, pratique pour les releases, pratique pour construire morceau par morceau et intégrer de même. Par exemple on peut segmenter la difficulté d'intégrer des Web Services en isolant les responsabilités suivantes dans des projets différents :
  • logique applicative,
  • liens avec la base de données,
  • interopérabilité,
  • distribution,
  • sécurité.
Cependant au fil des développements les projets évoluent, et les liens entre eux. On se retrouve avec une sorte de plat de spaghettis dont il nous manque une vue d'ensemble. Pour cela je n'ai pas trouvé mon bonheur dans des outils comme le Maven Dependency Plugin, plugin certes très utile, mais cantonné à des analyses projet par projet.

À défaut d'avoir trouvé un outil qui réponde exactement à mon besoin, j'en ai développé un moi-même. Ce n'est certes pas mon métier de faire ça, je ne vais donc pas aller plus loin pour cet outil que la bête réponse, non intrusive, au besoin initial.

C'est un programme qui scanne les fichiers pom.xml de mon espace de travail, et produit un diagramme UML.

Dépendances entre mes projets Maven.

Pour les détails :
  • le programme est en Java (le scan et la concaténation étaient au départ une tâche Ant),
  • le diagramme UML final est un fichier XML au format du logiciel StarUML,
  • le temps d'exécution (scan, analyse, génération) est de 10 secondes pour 113 projets.
Quelques différences de principe avec le Maven Dependency Plugin :
  • je m'appuie sur les sources des fichiers POM, et non sur les artifacts déjà installés dans le repository local - je peux donc aussi analyser les POMs de projets qui sont en erreur,
  • l'analyse porte sur l'espace de travail complet,
  • le rendu est graphique.
Je traduis ainsi en UML les liens entre projets Maven :
  • <parent> : héritage UML
  • <dependency> : association UML
  • <module> : agrégation UML
  • <plugin> : dépendance UML
Certes, le diagramme UML produit par défaut est illisible parce que surchargé. L'intérêt d'avoir un résultat lisible par un logiciel tiers, StarUML en l'occurrence, est qu'on peut ensuite y déplacer à la main des éléments et en supprimer, afin d'en extraire des sous-diagrammes orientés par domaine.

Voici par exemple un extrait du diagramme ci-dessus, limité à une vingtaine de projets, pour une couche applicative sur laquelle je bosse en ce moment.


Ce genre de petite documentation extraite des fichiers sources est bien utile. La cohérence est importante entre la documentation et le développement.

Pour en revenir à Maven, cette vue permet entre autres de visualiser les dépendances inutiles (quand A dépend de B qui depend de C, A ne devrait pas avoir à déclarer qu'il dépend de C), ou les incohérences entre les versions des bibliothèques.

Si quelqu'un connaît des outils plus évolués pour faire la même chose je suis preneur...

24 janvier 2009

Développement : Custom Maven Repository

Depuis 2007 j'utilise Artifactory comme repository Maven pour :
  1. déployer les bibliothèques tierces que je ne trouve pas dans des public Maven repositories tels que ceux d'Apache ou de Codehaus. De telles bibliothèques sont par exemple jvyaml, ou encore les outils Sun pour JMX, pourtant importants dès qu'on tape un peu dans du Lo4J 1.2.15 par exemple,
  2. pouvoir déployer mes propres artifacts, notamment ceux en version release.
Artifactory a de nombreux avantages. Il est facile à installer, et son interface HTML est pratique d'utilisation. Il est depuis peu en 2.0.0, version stable et dont l'IHM s'appuie sur Ajax à relativement bon escient. Je compare cela avec des versions d'Archiva livrée avec Maestro, par exemple.

Artifactory fonctionne très bien, et les déploiements Maven s'y font sans souci.

En revanche, il souffre d'un gros défaut : il fait systématiquement planter la Dedibox sur laquelle je l'ai installée.

C'est une Dedibox V1, avec un processeur C7 à 2 GHz et 1 Go de RAM, sous Debian. Les services que j'y ai installés sont :
  • BIND9
  • Apache 2.2 + PHP + MySQL
  • Subversion
  • Java 5 + Tomcat 5.5.20 pour : Confluence, Jira, Artifactory
  • scripts de backups
Tomcat5.5 démarre avec les options de mémoire suivantes : -Xmx512m -XX:MaxPermSize=256m

Concernant les fuites mémoires connues dans Jira je suis bien en Tomcat 5.5.15+ et j'ai bien -Dorg.apache.jasper.runtime.BodyContentImpl.LIMIT_BUFFER=true

Rien n'y fait, même en désactivant le service de backup (une tâche répétitive pilotée par un serveur J2EE, bonjour l'architecture) et en limitant les repositories à indexer, Artifactory me fait tomber la machine. Certes pas tout à fait à heure fixe, mais en moyenne cinq fois par semaine.

Plantage de machine, ce qui signifie notamment que le wiki et Subversion sont inaccessibles. De plus un redémarrage matériel est nécessaire, ce qui n'est jamais très bon.

Sachant d'autre part que -Xmx256m suffit à faire tourner Confluence et Jira sans Artifactory.

Alors pour le remplacer je vais regarder Nexus. Une comparaison trouvée par ici : Comparing Nexus and Artifactory évoque bien les différences de philosophie, notamment dans le stockage des artifacts. Faire une sauvegarde d'un repository Nexus (un répertoire, tâche pilotable par un bête script) semble bien plus simple que dans le cas d'Artifactory (une base de données JackRabbit, et surtout tâche à piloter par le serveur J2EE... N'importe quoi).

Si cela pouvait m'éviter d'avoir à dédier une machine puissante à ma solution de Custom Maven Repository...

12 janvier 2009

Rachat du nom de domaine Les Fûts

Eh oui, c'est bien parti.

Je ne mets pour l'instant qu'une page web d'attente : Les Fûts, histoire de commencer le référencement.

07 janvier 2009

Site de l'association graine d'école : pas Les Fûts

En 2006 l'idée des Fûts a germé. Des associations locales étaient vivement intéressées par le projet, comme Graine d'école, ou Le jardin sauvage. 

Et puis par manque de moyens au vu des ambitions, et aussi par un enchaînement de priorités autres, le projet les Fûts a capoté.

Pour Graine d'école j'avais fait une page HTML, qui se voulait provisoire, hébergée chez Free. 

Mais, depuis, l'association a fait son propre site avec Joomla! et a un nom de domaine en .com : http://graine-ecole.com/. Il est alimenté avec des nouvelles, des photos, il a un calendrier, des documents partagés... Bref il est vivant est c'est chouette. Du coup je viens de mettre une redirection « 301 », c'est-à-dire permanente,  de l'adresse http://grainedecole.free.fr/ vers le site.



Mon ancienne page se voit sur http://grainedecole.free.fr/old/


Association + groupe de rock

Jean-Charles, mon voisin, vient de passer pour qu'on discute d'un projet de site web d'association. 

Ben oui, je déterre occasionnellement le projet Les Fûts, sans velléité cette fois d'en faire lui-même un projet associatif.

Il me donne donc les prolégomènes de son attente, et là j'apprends qu'il y a deux sites à prévoir car deux entités conjointes : d'une part un groupe de rock, qui donne des concerts, vend des CDs, etc. dont les membres ont d'ores et déjà des espaces MySpace mais qui aimeraient éventuellement les restructurer, et d'autre part une association engagée dans des enjeux environnementaux, sorte de spin-off du groupe, association dont Jean-Charles est le président.

Ma première réaction par rapport à cette dissociation était de voir le site du groupe (site à petit budget) comme un site standard, avec tous les écueils de ce genre de projets : site vitrine, pas vraiment business driven, structure archi-classique, évolutions coûteuses, et solution parfois moins conviviale et dynamique qu'une solution gratuite.

Mais une autre direction pourrait être de définir les attentes en termes d'utilisations-types (on a commencé à parler de scénarios) et surtout de retours sur investissement, que ce ROI soit pécunier ou non (il y a la notoriété, les tremplins...). Il faudrait confronter les idées et surtout peut-être les retours d'expériences d'avoir jusqu'ici utilisé des espaces MySpace. De plus je me demande dans quelle mesure le groupe de rock évoqué ne pourrait pas être vu comme un partenaire artistique de l'association, ce qui à la limite inverserait la donne. Bref, voir quelles sont les attentes qui passent en avant.

J'en saurai davantage quand je rencontrerai les membres du groupe, à la fin de cette semaine.

06 janvier 2009

Restriction des accès SSH extérieurs par IP

Ce qui suit est générique, mais, pour savoir, la plate-forme considérée est une Debian.

Quand on met en place un serveur SSH sur un serveur pour en permettre l'administration à distance, on renseigne avec la ligne suivante le fichier /etc/ssh/sshd_config afin d'interdire à root de se connecter, car ce serait un beau trou de sécurité :
PermitRootLogin no
Il est également possible et facile de restreindre les accès par adresses IP pour le service SSH. Pour cela, renseigner dans le fichier /etc/hosts.allow :
sshd: adresses IP séparées par des espaces
Exemple :
sshd: 10.11.22.33 10.44.55.66 10.77.88.99
Et dans le fichier /etc/hosts.deny, renseigner :
sshd: ALL
On peut sécuriser l'accès à d'autres services que SSH, donner des classes d'adresses IP, etc.

En revanche, ne pas chercher la complication en mettant des noms d'hôtes, mais se cantonner aux adresses IP.

C'est une sécurisation fiable et simple. L'étape suivante est la configuration iptables, dont je ne traiterai pas ici.

Le schéma ci-dessous donne un exemple d'une personne derrière une « box » ADSL avec une adresse IP fixe (sinon, au premier changement d'IP, il ne sera plus possible d'administrer les serveurs !) et en mode routeur, qui administre deux serveurs, et souhaite interdire les accès SSH depuis d'autres machines.


Sur les deux serveurs il configurera un fichier hosts.deny avec sshd: ALL, et des fichiers hosts.allow :
  • sur la machine Serveur 1 : sshd: 10.11.22.33
  • sur la machine Serveur 2 : sshd: 10.11.22.33 10.44.55.66

Pour la petite histoire, tous les services n'ont pas un accès qui puisse être sécurisé par hosts.deny. En effet, il faut que le service concerné soit compatible avec les tcpwrappers, ce dont on peut s'assurer par la commande suivante par exemple pour sshd :
$ ldd `which sshd` | grep libwrap
$ ldd /usr/sbin/sshd | grep libwrap (plus précis, si which ne va pas)
Cette astuce fonctionne à peu près pour tous les services. Ainsi, ldd /usr/sbin/apache2 | grep libwrap montre que Apache 2 n'est pas compatible avec les tcpwrappers, et ne pourra donc pas voir son accès sécurisé par hosts.deny / hosts.allow.

Pour avoir un aperçu de qui cherche à se connecter en brute force par SSH sur vos machines, faites un tour dans /var/log/auth.log. Sans la sécurisation par IP on trouve en général des tentatives de connexions root, et toute une liste de tentatives avec des identifiants bidon (admin, test, virus...). 

Pour compter ces tentatives :
# grep Failed /var/log/auth.log | wc -l
Sur mes serveurs le nombre de tentatives varie selon les hébergeurs entre une par heure et 150 par heure.

04 janvier 2009

Millennium Falcon ™ en LEGO ®

Mon fils Emmanuel vient de finir la construction de son Millennium Falcon ™ en LEGO ®. Pour le bonhomme de sept ans qu'il est cela représente plusieurs jours de concentration. Je l'ai aidé à organiser son temps pour ne pas tourner chèvre, et à gérer les erreurs de montage, inévitables sur un tel chantier.


Le principal écueil de ces énormes jeux est bel et bien la tension nerveuse, qui va souvent crescendo et s'étale sur une longue durée. Ils sont en général conseillés aux plus grands.


Et voilà, « les copains pourront être épatés » comme il dit. On verra bien ce qu'ils en pensent ; nul doute que les plus simples Kapla continueront aussi de les intéresser. Car pour le Faucon Millénium, il semble approprié de le conserver tel quel...