Another Home Page Blog - sftphttps://blog.anotherhomepage.org/2009-10-04T09:33:00+02:00Transfert de fichier simple et sécurisé : sftp en chroot2009-10-04T09:33:00+02:002009-10-04T09:33:00+02:00Nils Ratuszniktag:blog.anotherhomepage.org,2009-10-04:/post/2009/10/04/Transfert-de-fichier-simple-et-sécurisé-:-sftp-en-chroot/<p>Meurs, FTP, meurs !</p>
<h2>C'est quoi ton problème ?</h2>
<p>Comme beaucoup de gens, pour transférer des fichiers sur un serveur web, j'utilise souvent <a href="http://fr.wikipedia.org/wiki/File_Transfer_Protocol">FTP</a>. Ce protocole possède plusieurs inconvénients :</p>
<ul>
<li>il faut ouvrir plusieurs ports dans le pare-feu, au moins deux (connexion de contrôle et de données)</li>
<li>le mot de passe transite en …</li></ul><p>Meurs, FTP, meurs !</p>
<h2>C'est quoi ton problème ?</h2>
<p>Comme beaucoup de gens, pour transférer des fichiers sur un serveur web, j'utilise souvent <a href="http://fr.wikipedia.org/wiki/File_Transfer_Protocol">FTP</a>. Ce protocole possède plusieurs inconvénients :</p>
<ul>
<li>il faut ouvrir plusieurs ports dans le pare-feu, au moins deux (connexion de contrôle et de données)</li>
<li>le mot de passe transite en clair sur le réseau, et même si on utilise <a href="http://fr.wikipedia.org/wiki/FTPS">FTPS</a>, qui chiffre la partie authentification, tous les clients et serveurs ne le supportent pas ou de manière boguée (voir chez <a href="http://forum.filezilla-project.org/viewtopic.php?f=2&t=7688">FileZilla</a> pour une explication)</li>
<li>les données transitent en clair (mince, le fichier config.php de mon appli avec les codes d'accès à la base de données...)</li>
<li>gestion du NAT catastrophique (du moins avec <a href="http://vsftpd.beasts.org/">Vsftpd</a>)</li>
</ul>
<p>Du coup, je cherche depuis plusieurs mois à éradiquer FTP de mes machines. Ce qui m'intéresse, c'est de pouvoir enfermer les utilisateurs dans une cage, de sorte qu'ils n'aient accès qu'à leurs données et pas à celles des autres, encore moins les autres fichiers et répertoires du serveurs. On appelle ceci un <a href="http://fr.wikipedia.org/wiki/Chroot">chroot</a>. Je faisais déjà ceci avec Vsftpd, j'espérais donc le faire avec la solution de remplacement. D'ailleurs, cette solution de remplacement était déjà toute trouvée : je désirais utiliser le serveur SFTP contenu dans le très bon logiciel <a href="http://www.openssh.com/fr/index.html">OpenSSH</a>. Maintenant, il me fallait réussir à créer des utilisateurs en leur empêchant d'avoir accès au shell, et en les confinant dans un chroot.</p>
<h2>Et t'as quoi comme solution ?</h2>
<p>Pour enlever l'accès au shell, très facile : tout système Unix qui se respecte possède soit un exécutable nommé <em>false</em>, soit un autre nommé <em>nologin</em>. D'ailleurs ce dernier est très simpliste, regardons sur, au hasard, un système NetBSD 5.0.1 :</p>
<div class="highlight"><pre><span></span><code><span class="n">nils</span><span class="nv">@tomb</span><span class="err">:</span><span class="o">~</span><span class="w"> </span><span class="err">$</span><span class="n">cat</span><span class="w"> </span><span class="o">/</span><span class="n">sbin</span><span class="o">/</span><span class="n">nologin</span><span class="w"> </span>
<span class="err">#!</span><span class="w"> </span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">sh</span><span class="w"></span>
<span class="n">echo</span><span class="w"> </span><span class="ss">"This account is currently not available."</span><span class="w"></span>
<span class="k">exit</span><span class="w"> </span><span class="mi">1</span><span class="w"></span>
</code></pre></div>
<p>Il suffit donc de remplacer le shell de l'utilisateur par le chemin vers nologin, et cette question est résolue.</p>
<p>Pour créer et maintenir un chroot, c'est une autre paire de manches. Dans Vsftpd c'était assez simple, et j'espérais trouver aussi simple. De nombreuses pages sur <a href="http://sublimation.org/scponly/wiki/index.php/Main_Page">Scponly</a> ou <a href="http://pizzashack.org/rssh/">rssh</a> expliquent comment faire un chroot pour un utilisateur n'ayant accès qu'à sftp ou scp, mais le jour où il faut mettre à jour l'OS, voire le migrer vers une version majeure plus récente (ou pourquoi pas en changer, comme passer d'un Linux à un BSD ou inversement, ou tout simplement changer de distribution Linux), le chroot doit être maintenu à jour. Et ça, je trouve que c'est totalement contre-productif, en tous cas du point de vue du sysadmin fainéant que nous avons tous en nous ;)</p>
<p>Et là, la lumière est arrivée, par <a href="http://undeadly.org/cgi?action=article&sid=20080220110039">ici</a>. Depuis la version 4.8, OpenSSH permet de créer des chroot, et n'oblige pas à recréer tout un environnement quand il s'agit de sftp. Exactement ce dont j'ai besoin ! Maintenant, reste à savoir quels systèmes disposent d'au moins OpenSSH 4.8.</p>
<p>Une petite liste non-exhaustive des systèmes chanceux :</p>
<ul>
<li>NetBSD 5.0.1</li>
<li>FreeBSD 7.2</li>
<li>Debian Lenny</li>
<li>Mac OS 10.5.8</li>
</ul>
<p>Une autre liste, mais de systèmes moins chanceux :</p>
<ul>
<li>CentOS 3,4,5.X</li>
<li>RHEL 3,4,5.X</li>
<li>Debian Etch</li>
</ul>
<p>Si votre système unix libre (ou pas, d'ailleurs, puisque j'ai listé Mac OS X) comporte OpenSSH, vous pouvez vérifier sa version par :</p>
<div class="highlight"><pre><span></span><code><span class="n">nils</span><span class="nv">@darkmoon</span><span class="err">:</span><span class="o">~</span><span class="w"> </span><span class="err">$</span><span class="n">ssh</span><span class="w"> </span><span class="o">-</span><span class="n">V</span><span class="w"></span>
<span class="n">OpenSSH_5</span><span class="mf">.1</span><span class="n">p1</span><span class="p">,</span><span class="w"> </span><span class="n">OpenSSL</span><span class="w"> </span><span class="mf">0.9.7</span><span class="n">l</span><span class="w"> </span><span class="mi">28</span><span class="w"> </span><span class="n">Sep</span><span class="w"> </span><span class="mi">2006</span><span class="w"></span>
</code></pre></div>
<p>(exemple pris sur un mac) En utilisant sshd au lieu de ssh, ça sera sans doute plus représentatif, mais l'option -V n'existe pas sur le serveur. La réponse retournée donnera quand même la version. Exemple, toujours sur le même mac :</p>
<div class="highlight"><pre><span></span><code><span class="n">nils</span><span class="nv">@darkmoon</span><span class="err">:</span><span class="o">~</span><span class="w"> </span><span class="err">$</span><span class="n">sshd</span><span class="w"> </span><span class="o">-</span><span class="n">V</span><span class="w"></span>
<span class="nl">sshd</span><span class="p">:</span><span class="w"> </span><span class="n">illegal</span><span class="w"> </span><span class="k">option</span><span class="w"> </span><span class="o">--</span><span class="w"> </span><span class="n">V</span><span class="w"></span>
<span class="n">OpenSSH_5</span><span class="mf">.1</span><span class="n">p1</span><span class="p">,</span><span class="w"> </span><span class="n">OpenSSL</span><span class="w"> </span><span class="mf">0.9.7</span><span class="n">l</span><span class="w"> </span><span class="mi">28</span><span class="w"> </span><span class="n">Sep</span><span class="w"> </span><span class="mi">2006</span><span class="w"></span>
<span class="k">usage</span><span class="err">:</span><span class="w"> </span><span class="n">sshd</span><span class="w"> </span><span class="o">[</span><span class="n">-46DdeiqTt</span><span class="o">]</span><span class="w"> </span><span class="o">[</span><span class="n">-b bits</span><span class="o">]</span><span class="w"> </span><span class="o">[</span><span class="n">-C connection_spec</span><span class="o">]</span><span class="w"> </span><span class="o">[</span><span class="n">-f config_file</span><span class="o">]</span><span class="w"></span>
<span class="w"> </span><span class="o">[</span><span class="n">-g login_grace_time</span><span class="o">]</span><span class="w"> </span><span class="o">[</span><span class="n">-h host_key_file</span><span class="o">]</span><span class="w"> </span><span class="o">[</span><span class="n">-k key_gen_time</span><span class="o">]</span><span class="w"></span>
<span class="w"> </span><span class="o">[</span><span class="n">-o option</span><span class="o">]</span><span class="w"> </span><span class="o">[</span><span class="n">-p port</span><span class="o">]</span><span class="w"> </span><span class="o">[</span><span class="n">-u len</span><span class="o">]</span><span class="w"></span>
</code></pre></div>
<p>Si votre système ne possède pas un OpenSSH assez récent, plusieurs possibilités s'offrent à nous :</p>
<ul>
<li>changer de système</li>
<li>mettre à jour vers la dernière version majeure si celle-ci possède une version assez récente</li>
<li>installer sa propre version d'OpenSSH ou récupérer le paquet qu'aurait fait quelqu'un de généreux</li>
</ul>
<p>La dernière solution est assez documentée pour CentOS et RHEL, pour faire ses propres RPM, mais j'ai décidé de ne pas la suivre, car cela pose le problème des mises à jour : de la même manière que maintenir un chroot ne me satisfait pas, passer mon temps à guetter les nouvelles versions d'OpenSSH pour compiler un paquet ne me plait pas plus. La deuxième solution s'avère sans doute la moins gênante selon les applications en production. De mon côté, j'ai choisi la première : migration de serveur dédié oblige, j'en ai profité pour élargir mes horizons dans le monde des unix libres et depuis quelques mois, ce blog tourne sous NetBSD. C'est donc avec cet OS que je vais décrire la manipulation de création de chroot.</p>
<h2>On passe à l'action ?</h2>
<p>Je pars du principe dorénavant que nous avons un système avec un OpenSSH 4.8 ou supérieur, que le serveur sshd est activé, que nous avons deux utilisateurs : root, et notre utilisateur habituel avec lequel nous faisons tout ce qui n'a pas besoin d'être fait en root. Le but est d'avoir un ou plusieurs utilisateurs supplémentaires, enfermés dans un répertoire défini, sans shell, et pouvant accéder à ce réperoire en sftp. On pourra, en supplément, faire en sorte que l'utilisateur accède à son compte sftp avec une clé (et éventuellement une phrase de passe) plutôt qu'un mot de passe.</p>
<p>Cela va se faire en modifiant dans un premier temps le fichier de configuration <em>/etc/ssh/sshd_config</em> (en tant que root, et le chemin peut varier selon le système). Cherchons la ligne contenant <em>sftp-server</em>, pour NetBSD elle ressemble à ceci :</p>
<div class="highlight"><pre><span></span><code>Subsystem sftp /usr/libexec/sftp-server
</code></pre></div>
<p>On constate que le serveur sftp est un programme externe. Nous allons le remplacer par le sous-système sftp de sshd :</p>
<div class="highlight"><pre><span></span><code>Subsystem sftp internal-sftp
</code></pre></div>
<p>J'ai donc remplacé <em>/usr/libexec/sftp-server</em> par <em>internal-sftp</em>. Allons ensuite à la fin du fichier, et ajoutons les directives suivantes :</p>
<div class="highlight"><pre><span></span><code>Match Group wwwusers
ChrootDirectory %h
ForceCommand internal-sftp
AllowTcpForwarding no
X11Forwarding no
</code></pre></div>
<p>Cela signifie que pour les utilisateurs du groupe <em>wwwusers</em>, je les emprisonne dans leur répertoire home, je les oblige à utiliser le sftp interne, et je les empêche d'utiliser les différentes techniques de forwarding habituellement à disposition avec sshd. j'aurais pu les chrooter ailleurs, d'autres sites indiquent par exemple <em>/chroot/%u</em>, où <em>%u</em> désigne le nom de l'utilisateur. Une fois ces modifications effectuées, il ne reste qu'à redémarrer le serveur ssh et à créer le groupe et les utilisateurs.</p>
<p>Petit aparté concernant NetBSD 5.01 : j'ai remarqué un bug sur cette version, qui doit aussi être présent dans la 5.0; il ne faut surtout rien ajouter au fichier de configuration <em>/etc/ssh/sshd_config</em> après cette directive, pas même un commentaire ! Si cela venait à arriver, la directive que nous venons d'ajouter serait tout simplement ignorée.</p>
<p>Créons le groupe :</p>
<div class="highlight"><pre><span></span><code><span class="n">root</span><span class="nv">@vhost</span><span class="err">:</span><span class="o">~</span><span class="w"> </span><span class="n">#groupadd</span><span class="w"> </span><span class="n">wwwusers</span><span class="w"></span>
</code></pre></div>
<p>Créons ensuite un utilisateur nommé test :</p>
<div class="highlight"><pre><span></span><code><span class="n">root</span><span class="nv">@vhost</span><span class="err">:</span><span class="o">~</span><span class="w"> </span><span class="n">#useradd</span><span class="w"> </span><span class="o">-</span><span class="n">m</span><span class="w"> </span><span class="o">-</span><span class="n">g</span><span class="w"> </span><span class="n">wwwusers</span><span class="w"> </span><span class="o">-</span><span class="n">s</span><span class="w"> </span><span class="o">/</span><span class="n">sbin</span><span class="o">/</span><span class="n">nologin</span><span class="w"> </span><span class="n">test</span><span class="w"></span>
</code></pre></div>
<p>Attribuons un mot de passe à cet utilisateur :</p>
<div class="highlight"><pre><span></span><code><span class="n">root</span><span class="nv">@vhost</span><span class="err">:</span><span class="o">~</span><span class="w"> </span><span class="n">#passwd</span><span class="w"> </span><span class="n">test</span><span class="w"></span>
<span class="n">Changing</span><span class="w"> </span><span class="n">password</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">test</span><span class="p">.</span><span class="w"></span>
<span class="k">New</span><span class="w"> </span><span class="nl">Password</span><span class="p">:</span><span class="w"></span>
<span class="n">Retype</span><span class="w"> </span><span class="k">New</span><span class="w"> </span><span class="nl">Password</span><span class="p">:</span><span class="w"></span>
</code></pre></div>
<p>(le mot de passe est tapé en aveugle, bien entendu). Ensuite, assurons-nous que le répertoire home de l'utilisateur appartient non pas à l'utilisateur mais à root, avec des permissions en 755. Si ce n'est pas le cas, on y remédie de cette manière :</p>
<div class="highlight"><pre><span></span><code><span class="n">root</span><span class="nv">@vhost</span><span class="err">:</span><span class="o">~</span><span class="w"> </span><span class="n">#chown</span><span class="w"> </span><span class="nl">root</span><span class="p">:</span><span class="n">wheel</span><span class="w"> </span><span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">test</span><span class="w"></span>
<span class="n">root</span><span class="nv">@vhost</span><span class="err">:</span><span class="o">~</span><span class="w"> </span><span class="n">#chmod</span><span class="w"> </span><span class="mi">755</span><span class="w"> </span><span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">test</span><span class="w"></span>
</code></pre></div>
<p>On notera que pour les OS Linux, on indique <em>root:root</em> par rapport à NetBSD qui n'a pas de groupe <em>root</em> mais un groupe <em>wheel</em>. A ne pas oublier aussi, seul <em>/home/test</em> appartient à root, pas les fichiers et répertoires à l'intérieur (i.e. pas de chmod/chown -R)</p>
<p>Depuis une autre machine, vérifions que nous pouvons nous connecter en sftp :</p>
<div class="highlight"><pre><span></span><code><span class="n">sftp</span><span class="w"> </span><span class="n">test</span><span class="nv">@vhost</span><span class="w"></span>
<span class="n">Connecting</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="n">vhost</span><span class="p">...</span><span class="w"></span>
<span class="nl">Password</span><span class="p">:</span><span class="w"></span>
<span class="n">sftp</span><span class="o">></span><span class="w"> </span><span class="n">ls</span><span class="w"></span>
<span class="n">sftp</span><span class="o">></span><span class="w"> </span><span class="n">pwd</span><span class="w"></span>
<span class="n">Remote</span><span class="w"> </span><span class="n">working</span><span class="w"> </span><span class="nl">directory</span><span class="p">:</span><span class="w"> </span><span class="o">/</span><span class="w"></span>
</code></pre></div>
<p>Ici, on remarque que je me suis déjà connecté à cette machine avant, puisqu'on ne me réclame pas d'accepter de clé. On remarque aussi qu'on est directement dans le répertoire / et qu'il n'y a rien, la commande <em>pwd</em> indique <em>/</em> et pas <em>/home/test</em>. Si on crée dans ce répertoire un deuxième <em>/home/test</em>, sftp nous y emmènera directement dedans. De plus, on note qu'on ne peut pas créer ou ajouter de répertoire/fichier (normal, le répertoire appartient à root). Créons donc, sur le serveur, le second “home” de l'utilisateur :</p>
<div class="highlight"><pre><span></span><code><span class="n">root</span><span class="nv">@vhost</span><span class="err">:</span><span class="o">~</span><span class="w"> </span><span class="n">#cd</span><span class="w"> </span><span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">test</span><span class="w"></span>
<span class="n">root</span><span class="nv">@vhost</span><span class="err">:</span><span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">test</span><span class="w"> </span><span class="err">#</span><span class="w"> </span><span class="n">mkdir</span><span class="w"> </span><span class="o">-</span><span class="n">p</span><span class="w"> </span><span class="n">home</span><span class="o">/</span><span class="n">test</span><span class="w"></span>
<span class="n">root</span><span class="nv">@vhost</span><span class="err">:</span><span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">test</span><span class="w"> </span><span class="err">#</span><span class="w"> </span><span class="n">chown</span><span class="w"> </span><span class="o">-</span><span class="n">R</span><span class="w"> </span><span class="nl">test</span><span class="p">:</span><span class="n">wwwusers</span><span class="w"> </span><span class="n">home</span><span class="w"></span>
<span class="n">root</span><span class="nv">@vhost</span><span class="err">:</span><span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">test</span><span class="w"> </span><span class="err">#</span><span class="w"> </span><span class="n">chmod</span><span class="w"> </span><span class="mi">755</span><span class="w"> </span><span class="n">home</span><span class="w"></span>
</code></pre></div>
<p>Reconnectons-nous à notre serveur sftp :</p>
<div class="highlight"><pre><span></span><code><span class="n">sftp</span><span class="w"> </span><span class="n">test</span><span class="nv">@vhost</span><span class="w"></span>
<span class="n">Connecting</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="n">vhost</span><span class="p">...</span><span class="w"></span>
<span class="nl">Password</span><span class="p">:</span><span class="w"></span>
<span class="n">sftp</span><span class="o">></span><span class="w"> </span><span class="n">ls</span><span class="w"></span>
<span class="n">sftp</span><span class="o">></span><span class="w"> </span><span class="n">pwd</span><span class="w"></span>
<span class="n">Remote</span><span class="w"> </span><span class="n">working</span><span class="w"> </span><span class="nl">directory</span><span class="p">:</span><span class="w"> </span><span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">test</span><span class="o">/</span><span class="w"></span>
</code></pre></div>
<p>Je peux maintenant créer des répertoires, envoyer des fichiers, en rapatrier d'autres. Mission accomplie !</p>