Paramétrer SSH et sécuriser ses transactions FTP et SVN dans un tunnel SSH.

Category: Configuration,LinuxPh. Ivaldi @ 11 h 46 min

Résumer de l'article

Cette article décrit comment paramétrer un serveur SSH pour n'accepter que les authentifications par clés et comment utiliser cette configuration pour mettre en place un serveur SVN sécurisé par un tunnel SSH. Nous verrons au passage qu'un serveur ftp sécurisé (sftp) est automatiquement disponible et comment utiliser rsync pour synchroniser des données entre deux machines ; le tout à travers un tunnel SSH.

L'OS utilisé est GNU/Linux Debian , mais la transposition à un autre environnement unix-like ne devrait pas poser de difficulté.

Prérequis

Installation

On commence par installer subversion, si l'on veut avoir un serveur svn, et openssh-server qui fournira entre autre un serveur ssh et sftp :
apt-get install subversion openssh-server.

Création d'un utilisateur pouvant s'authentifier par clef

Clefs ssh

Pour ce faire, on crée sur le poste client (le poste de celui qui va se connecter en ssh) une pair de clés; une privée à conserver précieusement et une publique que vous pouvez donner à la terre entière.
La commande ssh-keygen -t rsa -b 1024 -f ~/.ssh/key-1_rsa crée dans le répertoire ~/.ssh deux clefs : key-1_rsa et key-1_rsa.pub.
La clef key-1_rsa est la clef privée qu'il ne faut donner à personne et que vous devez garder précieusement à partir du moment où elle est utilisée pour signer/chiffrer des messages, des fichiers, des partitions etc ou encore pour se connecter à un serveur distant.
La clé key-1_rsa.pub est la clef publique correspondante que vous pouvez distribuer à tout le monde; elle permettra aux autres personnes de, par exemple, vérifier les messages que vous aurez signé avec la clé privée correspondante.

Utilisateur sur le serveur

Il faut maintenant créer un utilisateur, ssh_user par exemple mais cela pourrait être root, sur le poste serveur (celui auquel vous voulez vous connecter) tel que son fichier ~/.ssh/authorized_keys contienne la clef public; tout possesseur de la clef privée correspondante pourra alors se connecter au serveur en tant que ssh_user --d'où l'importance de garder précieusement sa clef privée--.
Sur le poste serveur:

  1. on crée l'utilisateur ssh_user : adduser ssh_user ;
  2. on devient ssh_user : su ssh_user ;
  3. si le répertoire ~/.ssh n'existe pas, on le crée: mkdir ~/.ssh ;
  4. on crée le fichier authorized_keys : touch ~/.ssh/authorized_keys ;
  5. et on restreint les droits de lecture : chmod 600 ~/.ssh/authorized_keys

Depuis le poste client, on envoie la clef publique sur le serveur par le moyen de son choix. En ssh, par exemple, ça donne :
scp ~/.ssh/key-1_rsa.pub root@ip_du_serveur:/home/ssh_user/.ssh/
Il faut ensuite entrer la clef publique dans le fichier /home/ssh_user/.ssh/authorized_keys; on peut encore utiliser ssh depuis le poste client :
ssh root@ip_du_serveur "cat /home/ssh_user/.ssh/key-1_rsa.pub >> /home/ssh_user/.ssh/authorized_keys
...ou utiliser son éditeur de texte préféré pour copier/coller le contenu du fichier.

L'utilisateur ssh_user est maintenant prêt... il ne reste plus qu'à configurer le serveur ssh pour qu'il n'autorise que les authentifications par clés.

Configurer SSH pour n'autoriser que les authentifications par clef

Voici un exemple de ce qu'il peut se trouver dans le fichier de configuration /etc/ssh/sshd_config, il est suffisamment commenté pour pouvoir l'adapter à des besoins spécifiques:

# Le port utilisé
Port 2564

# Version de ssh utilisée
Protocol 2

# Clefs système pour le protocol version 2
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_dsa_key

# Spécifie si sshd sépare les privilèges en créant un processus fils
##non privilégié pour prendre en charge le trafic réseau entrant.
UsePrivilegeSeparation yes

# Le but de la regénération est d'éviter le décryptage de sessions capturées
##en s'introduisant plus tard sur la machine et en volant la clef.
##Pour la version 1 seulement !
KeyRegenerationInterval 3600
# Définit le nombre de bits de la clef éphémère pour la version 1 du protocole.
ServerKeyBits 768

# Donne le code de facilité utilisé lors de l'enregistrement
##des messages du démon sshd.
SyslogFacility AUTH
LogLevel INFO

# Authentification:
LoginGraceTime 120

# Spécifie si root peut se connecter par ssh(1).
##L'argument est « yes », « without-password »,
##« forced-commands-only » ou « no ».
PermitRootLogin  without-password

#Spécifie si sshd doit vérifier les modes et le propriétaire
##des fichiers de l'utilisateur et du répertoire de base
##de l'utilisateur avant d'accepter une connexion.
StrictModes yes

#Spécifie si on autorise la pure authentification RSA.
##Version 1 du protocole seulement !!
RSAAuthentication yes

# Une option mystérieuse qui date de l'éqpoque où seulement le DSA
##était reconnu par SSH 2
##Considérée comme un alias de PubkeyAuthentication ?!
DSAAuthentication yes
PubkeyAuthentication yes

# Spécifie le fichier contenant les clefs publiques à utiliser
##pour l'authentification d'un utilisateur.
AuthorizedKeysFile %h/.ssh/authorized_keys

# ne pas utiliser les fichiers ~/.rhosts et ~/.shosts
IgnoreRhosts yes

# On n'autorise une authentification par rhosts ou /etc/hosts.equiv
##suite à une authentification de machine RSA réussie.
##SSH 1 seulement !
RhostsRSAAuthentication no

# La même chose en SSH 2
HostbasedAuthentication no

# Spécifie si on autorise l'authentification par stimulation-réponse
ChallengeResponseAuthentication no

# Spécifie si l'authentification par mot de passe est autorisée.
PasswordAuthentication no

# Un pur et dur linuxien ou pas ?
X11Forwarding no
# X11DisplayOffset 10

# Spécifie si sshd doit afficher le contenu du fichier /etc/motd
##quand un utilisateur se connecte en mode interactif.
PrintMotd no

#Spécifie si sshd doit afficher la date et l'heure de la
##dernière connexion de l'utilisateur.
PrintLastLog yes

# Spécifie si le système doit envoyer des messages TCP
##de maintien de la connexion.
##Évite les utilisateurs fantômes lors de connexions interrompues
TCPKeepAlive yes

#Banner /etc/issue.net

# Autorise le client à passr des variables locales
AcceptEnv LANG LC_*

# N'autoriser que certains utilisateurs à se connecter.
AllowUsers XXX YYY ZZZ svn

# Configure un sous-système externe
##SSH 2 seulement !!
##Ici on autorise les connection sftp
Subsystem sftp /usr/lib/openssh/sftp-server

#Autorise le « Pluggable Authentication Module interface ».
##man sshd_config pour plus d'info !
UsePAM yes

# Local Variables:
# mode:shell-script
# comment-column:0
# End:
  

Attention !

Avant de tester la nouvelle configuration du serveur ssh il faut bien s'assurer que le port utilisé, ici le port 2564 (il vaut mieux ne pas utiliser le port standard pour des raisons évidentes de sécurité), soit bien ouvert. La commande netstat -lp --inet | grep ssh doit retourner quelque chose comme:

tcp        0      0 *:2564                  *:*                     LISTEN      22952/sshd
  

Pour ouvrir le port 2564, sous iptable par exemple, il faut avoir (chez moi c'est dans le fichier /etc/network/if-pre-up.d/iptables-star, mais ça peut être ailleurs ;-) ):

# SSH In
iptables -t filter -A INPUT -p tcp --dport 2564 -j ACCEPT
# SSH Out
iptables -t filter -A OUTPUT -p tcp --dport 2564 -j ACCEPT
  

Vérifier aussi que l'utilisateur ssh que vous avez créé est bien mentionné dans les AllowUsers du fichier /etc/ssh/sshd_config.

On peut maintenant tester la nouvelle configuration du serveur ssh:

* on redémarre le service ssh du poste serveur :
/etc/init.d/ssh restart
* on se connecte depuis le poste client :
ssh -p 2564 -i ~/.ssh/key-1_rsa ssh_user@ip_du_serveur

Ça devrait marcher...
À la création de la clef, si l'on n'entre pas de passphrase, aucun mot de passe n'est demandé pour se connecter au serveur; cela peut être utile pour lancer des taches planifiées sur le serveur depuis un poste client...

Serveur ftp sécurisé : sftp (Secure File Transfer Protocol)

C'est un peu déroutant, mais le sftp ressemble au ftp, s'utilise comme le ftp mais n'est pas du ftp ! En fait c'est un protocole basé sur scp, qui est encore différent du ftps et aussi différent du ftp over ssh; pour plus d'information, on peut lire en anglais cet article.
L'important pour nous c'est qu'il s'utilise comme du ftp (même les pauvres utilisateurs de Windows n'y voient que du feu), mais que tout ce qui transite dans les tuyaux est crypté (chiffré en français, svp)... Personnellement je n'utilise que du sftp et, pour les clients que j'héberge du ftps qui est aussi sécurisé mais ne donne pas accès au shell.
La bonne nouvelle dans cette histoire est, qu'avec la configuration que j'ai donné, le serveur sftp est déjà opérationnel !!
Pour s'en convaincre, il suffit de lancer la commande suivante depuis le poste client :
sftp -oPort=2564 -oIdentityFile=~/.ssh/key-1_rsa ssh_usert@ip_du_serveur

Synchroniser des données distants à travers un tunnel SSH

Rien de plus simple avec le programme rsync du paquet éponyme :
/usr/bin/rsync -auv --numeric-ids --perms -e "/usr/bin/ssh -p 2564 -i ~/.ssh/key-1_rsa" SOURCE/ ssh_user@IP_MARCHINE_DISTANTE:DESTINATION

Cette commande va synchroniser le répertoire distant DESTINATION (pas de « / » à la fin) de la machine distante IP_MARCHINE_DISTANTE avec le répertoire local SOURCE/ (ne pas oublier le « / ») en respectant les droits de tous les fichiers/répertoires. S'il l'on veut une véritable synchronisation, avec destruction des fichiers inexistant en local sur la machine distante, il faut ajouter l'option --delete.
Avec une passphrase vide et les droits root on peut planifier une sauvegarde rudimentaire.

SVN via SSH

Pour avoir du SVN sécurisé dans un tunnel SSH c'est un peu plus délicat mais l'essentiel est déjà en place...

Création d'un « utilisateur SVN via ssh » un peu spécial

On commence par créer un utilisateur spécial, le seul utilisateur à pourvoir se connecter en svn; on verra que l'on peut quand même différencier les utilisateurs en fonction de clef d'authentification utilisée...
Il suffit donc, dans un premier temps, de procéder exactement comme précédemment:

  • Créer l'utilisateur svn
  • Créer le fichier vierge /home/svn/.ssh/authorized_keys avec les bons droits ;
  • Créer autant de clefs qu'il y a d'utilisateurs svn (on peut laisser ici la passphrase vide pour les utilisateurs SVN, cela permet de se connecter sans mot de passe et n'affecte pas trop la sécurité).
    Par la suite on supposera de notre clef privée se nomme key-1_svn_rsa.
  • Copier le contenu de toutes les clefs dans le fichier /home/svn/.ssh/authorized_keys ;

Finalement on a dans le fichier /home/svn/.ssh/authorized_keys quelque chose comme

ssh-rsa ABCZ...ADDE= svn@machine
ssh-rsa UTFH...UDSA= svn@machine
etc...
  

Afin de personnaliser les clefs et de restreindre l'accès ssh des utilisateurs de ces clefs aux seules actions svn on peut modifier ainsi le contenu du fichier /home/svn/.ssh/authorized_keys:

command="/usr/bin/svnserve -t -r MON_DOSSIER_SVN/repos" ssh-rsa ABCZ...ADDE= USER1
command="/usr/bin/svnserve -t -r MON_DOSSIER_SVN/repos" ssh-rsa UTFH...UDSA= USER2
etc...
  

L'assignation command="/usr/bin/svnserve -t -r MON_DOSSIER_SVN/repos" permet ici de spécifier un shell particulier à l'utilisateur de la clef correspondante, ainsi que de fixer le répertoire SVN racine, nous en reparlerons (MON_DOSSIER_SVN est évidemment à modifier suivant votre convenance).
Ainsi MON_DOSSIER_SVN/repos sera la racine du serveur SVN des utilisateurs USER1 et USER2 ; tout chemin donné lors d'une connexion SVN sera relatif à ce chemin.
Mais on peut mieux faire....

command="/usr/bin/svnserve -t -r MON_DOSSIER_SVN/repos --tunnel-user=USER1" ssh-rsa ABCZ...ADDE= USER1
command="/usr/bin/svnserve -t -r MON_DOSSIER_SVN/repos --tunnel-user=USER2" ssh-rsa UTFH...UDSA= USER2
etc...
  

L'option --tunnel-user=USER1 permet de spécifier l'identifiant de l'utilisateur de la clef correspondante. Ainsi, un utilisateur se connectant en tant que svn avec la première clef sera en identifié comme étant USER1 ; si l'authentification se fait avec la deuxième clé, ce sera USER2 etc.
Et ce n'est pas tout !
Par mesure de sécurité, on peut encore restreindre les marges de manœuvre des utilisateurs SVN en ajoutant des options SSH :

command="/usr/bin/svnserve -t -r MON_DOSSIER_SVN/repos --tunnel-user=USER1",no-port-forwarding,no-pty,no-agent-forwarding,no-X11-forwarding ssh-rsa ABCZ...ADDE= USER1
command="/usr/bin/svnserve -t -r MON_DOSSIER_SVN/repos --tunnel-user=USER2",no-port-forwarding,no-pty,no-agent-forwarding,no-X11-forwarding ssh-rsa UTFH...UDSA= USER2
etc...
  

Création d'un premier projet sous contrôle SVN via SSH

Sans vraiment trop d'effort, le serveur SVN sécurisé est opérationnel... il ne reste plus qu'à créer notre premier projet. Pour rappel, on travaille sur le poste serveur.

On peut découper le dépôt en deux parties : une partie publique que tout le monde pourra voir et télécharger, par exemple avec WebSVN, et une partie privée qui ne sera accessible qu'aux personnes autorisées. Cela simplifiera les configurations ultérieures.
mkdir -p MON_DOSSIER_SVN/repos/public
mkdir -p MON_DOSSIER_SVN/repos/private
On change le propriétaire et le groupe du dépôt:
chown -R svn\: MON_DOSSIER_SVN/repos
On donne les droits convenables, avec un umask de 2 (SGID positionné à 2000 pour que tous les fichiers endossement l'identité du groupe svn):
chmod -R 2770 MON_DOSSIER_SVN/repos
On peut alors créer un premier projet mon_premier_projet, publique par exemple:

mkdir "MON_DOSSIER_SVN/repos/public/mon_premier_projet"
mkdir -p /tmp/repo/branches
mkdir /tmp/repo/tags
mkdir /tmp/repo/trunk
svnadmin create --fs-type fsfs "MON_DOSSIER_SVN/repos/public/mon_premier_projet"
svn import /tmp/repo file://"MON_DOSSIER_SVN/repos/public/mon_premier_projet" -m "initial import"
rm -rf /tmp/repo
  

Pour s'éviter d'avoir à retaper ces commandes à chaque création de projet on peut créer un petit script shell, disons add-svn-project, qui, en plus de créer le projet donné en paramètre (add-svn-project mon_premier_projet), nous demande si le projet est publique ou privé...

#!/bin/bash

## À configurer:
SVN_ROOT_DIR="MON_DOSSIER_SVN/repos/" # Doit se terminer pas un /
## Fin configuration

[ $# -eq 0 ] && {
    echo "Usage : $0 nom_du_projet"
    exit 1
}

REP='indet'

test_rep() {
    ( [ "$REP" = "private" ] || [ "$REP" = "public" ] ) && echo true
}

PCR='X'
while [ ! `test_rep $PCR` ]
do
    printf "Private or publiC project ? [P/c] ";read PCR
    # [ "$PCR"=='' ] && PCR='P'
    case "$PCR" in
        c|C)
            echo -e '\nPublic Site'
            REP='public'
            ;;
        p|P|'')
            echo -e '\nPrivate Site'
            REP='private'
            ;;
    esac
    if [ ! `test_rep` ]
    then
        echo -e '\nP=Private ; C=publiC'
    fi
done


proj_dir="${SVN_ROOT_DIR}${REP}/${1}"

mkdir "$proj_dir"
mkdir -p /tmp/repo/branches
mkdir /tmp/repo/tags
mkdir /tmp/repo/trunk
svnadmin create "$proj_dir" --fs-type fsfs
svn import /tmp/repo file://"$proj_dir" -m "initial import"
rm -rf /tmp/repo

chown -R svn:www-data "$proj_dir"
chmod -R 2770 "$proj_dir"
  

Première connexion et premier commit

On peut maintenant télécharger sur un poste client, disposant de la clef privée adéquat, le projet mon_premier_projet sous contrôle SVN. Ceci se fera au travers d'un tunnel SSH, sur le port 2564, avec authentification par clefs uniquement. La commande devrait ressembler à ceci :
SVN_SSH="ssh -p 2564 -i ~/.ssh/key-1_svn_rsa" svn co svn+ssh://svn@MON_DOMAINE.fr/mon_premier_projet ./mon_premier_projet

Pour tester un premier commit :
cd mon_premier_projet && touch trunk/essai.txt
svn add trunk/essai.txt
et pour le commit il ne faut pas oublier de préciser les options de connexion SSH dans la variable SVN_SSH :
SVN_SSH="ssh -p 2564 -i ~/.ssh/key-1_svn_rsa" svn commit -m 'Description du commit...'

Pour ne pas avoir à retaper à chaque fois les options SSH on peut créer un alias par la commande :
alias ssvn='SVN_SSH="ssh -p 2564 -i ~/.ssh/key-1_svn_rsa" svn'

Le commit précédent devient alors tout simplement :
ssvn commit -m 'Description du commit...'

Pour que l'alias soit permanent, il suffit de mettre la commande de création d'alias dans son fichier ~/.bashrc.

Installation et configuration de WebSVN

Rien de plus à dire que cet excellant article. ;-)

Étiquettes : , ,


WebSVN pour dépôts publics et privés en même temps

Category: Configuration,PHPPh. Ivaldi @ 11 h 16 min

Sommaire de la page

Résumé

Suite à l'article décrivant, entre autre, comment utiliser SVN dans un tunnel SSH nous verrons ici comment configurer le serveur Apache pour donner un accès anonyme en lecture seul aux dépôts publics et configurer WebSVN de tel sorte que les dépôts publics et privés se trouvent sur la même page WebSVN sans avoir à entrer de mot de passe pour accéder aux dépôts publics ; ce qui n'est pas un comportement prévu originellement par WebSVN.
À la fin de cet article vous serez donc en mesure de configurer une instance WebSVN comme celle de ma page de projets sous contrôle SVN (j'utilise git maintenant mais l'article reste valable) pour laquelle j'ai créé un projet privé fictif accessible seulement à l'utilisateur demo, sans mot de passe.

Structure du dépôt

Dans cet article, on va supposer que les dépôts Subversion sont dans Quelque_Part/svn/repos/ ; ce répertoire contient lui même deux sous-répertoires :

  • Quelque_Part/svn/repos/publics contient les dépôts accessibles :
    • en lecture par tout le monde, via SVN ou WebSVN ;
    • en écriture pour ceux dont la clef SSH a été autorisée et seulement via SSH.
  • Quelque_Part/svn/repos/private contient les dépôts accessibles :
    • en lecture (via WebSVN) sur authentification par mot de passe ;
    • en écriture seulement pour ceux dont la clef SSH a été autorisée ;

Configurations

Dans la première partie nous présenterons une configuration très standard de WebSVN puis, dans la seconde partie, sera exposé une configuration plus ergonomique. Si vous n'êtes pas familier avec le fonctionnement de WebSVN, il est fortement conseillé de lire la première partie.

Configuration Standard

Cette configuration se retrouve partout sur le Web, elle n'a rien de bien originale. En revanche, elle n'ait pas toujours bien expliquée et je vais essayer d'éclaircir son fonctionnement.

Configuration de base d'Apache

Afin que WebSVN, qui est écrit en PHP faut-il le rappeler, puisse accéder à la configuration des dépôts SVN il faut installer le module libapache2-svn par la commande apt-get install libapache2-svn. L'utilisation de ce module permet d'accéder aux dépôts SVN à travers le protocole HTTP ; voici comment l'utiliser dans la configuration d'Apache.

<Location /public>
   Options Indexes +FollowSymLinks MultiViews
   DAV svn
   SVNParentPath Quelque_Part/svn/repos/public/
   SVNListParentPath on
   allow from all
   <LimitExcept GET PROPFIND HEAD OPTIONS REPORT CHECKOUT>
      deny from all
   </LimitExcept>
</Location>
  

Le module est chargé par l'instruction DAV svn ;
SVNParentPath Quelque_Part/svn/repos/public/ définit le répertoire, dit parent, contenant les dépôts ;
L'instruction SVNListParentPath on autorise à lister l'ensemble des dépôts du répertoire parent ; allow from all pour que tout le monde puisse accéder aux dépôts ;

<LimitExcept GET PROPFIND HEAD OPTIONS REPORT CHECKOUT>
   deny from all
</LimitExcept>
interdit tout accès sauf les opérations de lecture du dépôt.

Pour les dépôts privés voici comment l'on peut configurer Apache :
<Location /private>
   DAV svn
   SVNParentPath Quelque_Part/svn/repos/private/
   SVNListParentPath on

   Require valid-user
   AuthType Basic
   AuthName "Private Subversion Repository"
   AuthUserFile /etc/apache2/passwords/svn-access.pwd
</Location>
  

Les instructions sont identiques aux précédentes sauf qu'ici on spécifie qu'il est obligatoire d'être authentifié pour accéder aux dépôts. Pour créer le fichier d'authentification on utilise la commande
htpasswd -c /etc/apache2/passwords/svn-access.pwd Nom_Utilisateur
Pour ajouter un utilisateur il suffit de taper la même commande sans l'option -c. Plus d'informations en faisant htpasswd --help
Avec une telle configuration on peut accéder à tous les dépôts publics à l'adresse http://VOTRE_NOM_DE_DOMAINE/public/ sans authentification et à tous les dépôts privés à l'adresse http://VOTRE_NOM_DE_DOMAINE/private/ après authentification.
Il est aussi possible de récupérer la dernière version d'un dépôt public avec la commande
svn co http://VOTRE_DOMAINE/public/NOM_DU_DEPÔT/.
Toutes tentatives d'accès via svn aux dépôts privés sera rejetées ! Il ne reste plus qu'à faire communiquer Apache et SVN (via le programme svnserver) pour que l'utilisateur authentifié par Apache hérite de droits éventuels spécifiques aux dépôts. C'est l'objet de la section suivante.

Communication entre Apache et SVN

Nous voudrions maintenant que l'utilisateur demo, authentifié par Apache via le fichier svn-access.pwd, puisse voir le dépôt privé demonstration mais pas les autres dépôts... Rien de plus simple...
Il suffit de créé, par exemple, le fichier Quelque_Part/svn/repos/authz contenant les lignes suivantes

[demonstration:/]
demo=r
  

et de rajouter la ligne AuthzSVNAccessFile "Quelque_Part/svn/repos/authz" dans la configuration d'apache:

<Location /private>
   DAV svn
   SVNParentPath Quelque_Part/svn/repos/private/
   SVNListParentPath on

   Require valid-user
   AuthType Basic
   AuthName "Private Subversion Repository"
   AuthUserFile /etc/apache2/passwords/svn-access.pwd
   AuthzSVNAccessFile "Quelque_Part/svn/repos/authz"
</Location>
  

Après avoir redémarré le serveur Apache (par apache2ctl graceful sur un serveur en production) on doit pouvoir accéder au dépôt http://VOTRE_NOM_DE_DOMAINE/private/demonstration après authentification mais l'utilisateur demo n'aura pas accès aux autres dépôts, même en lecture.
En revanche, il ne semble plus possible d'avoir la liste des dépôts accessibles depuis l'adresse http://VOTRE_NOM_DE_DOMAINE/private/, si quelqu'un sait comment y remédier qu'il n'hésite pas à me contacter ou à laisser un commentaire...
Plus d'informations sur les autorisations basées sur le « path » se trouvent ICI. Attention, comme cela est souligné dans l'article, c'est gourmand en ressource.
Pour ma part, je ne donne aucun accès aux dépôts privés en http(s) puisque les utilisateurs de cet espace travaillent dans un tunnel ssh avec authentification par clé.

Configuration d'Apache pour WebSVN

Après avoir installé WebSVN, par apt-get install websvn il faut configurer Apache. En exclusivité pour PIPRIME.FR, voici la configuration utilisée par PIPRIME.FR sur svn.piprime.fr :

<VirtualHost *:80>
   ServerName svn.piprime.fr
   ServerAdmin adresse_courriel@sfr.fr
   DocumentRoot /usr/share/websvn

   <Directory />
      AllowOverride None
      Order allow,deny
      deny from all
   </Directory>

   <Directory /usr/share/websvn>
      AllowOverride None
      DirectoryIndex index.php
      Options FollowSymLinks
      Order allow,deny
      Allow from all
      php_admin_value safe_mode off
   </Directory>

   <Location /public>
      Options Indexes +FollowSymLinks MultiViews
      DAV svn
      SVNParentPath Quelque_Part/svn/repos/public/
      SVNListParentPath on
      AuthType Basic
      AuthName "Subversion Repository"
      AuthUserFile /etc/apache2/passwords/svn-access.pwd
      order allow,deny
      allow from all
      <LimitExcept GET PROPFIND HEAD OPTIONS REPORT CHECKOUT>
         deny from all
      </LimitExcept>
   </Location>

   # Logs:
   LogLevel warn
   ErrorLog /var/log/apache2/svn.piprime.fr.error.log
   CustomLog /var/log/apache2/svn.piprime.fr.access.log combined
   ServerSignature On
</VirtualHost>
  
Il n'y a rien de spécial à ajouter, il faut maintenant configurer WebSVN à proprement dit.

Configuration de WebSVN

La configuration de WebSVN ne semble pas poser pas de problèmes particuliers, les comentaires sont assez explicites :

<?php
// Pour avoir la coloration syntaxique du code
$config->setEnscriptPath("/usr/bin");
$config->setSedPath("/bin");
$config->useEnscript();

// La racine des dépôts publics
$config->parentPath("Quelque_Part/svn/repos/public");
// La racine des dépôts privés
$config->parentPath("Quelque_Part/svn/repos/private");
// Le fichier d'autorisation/authentification SVN
$config->useAuthenticationFile('Quelque_Part/svn/repos/authz');

// Le gabarit utilisé
$config->setTemplatePath("$locwebsvnreal/templates/calm/");
/* $config->setTemplatePath("$locwebsvnreal/templates/BlueGrey/"); */

// Pour ne pas avoir l'arborescence complète du projet pendant la navigation
// L'affichage est plus rapide avec ce comportement...
$config->useFlatView();

// Les projets sont, en général, codés en utf-8.
$config->setContentEncoding('utf-8');

// Autorise la génération des « tarballs »
$config->allowDownload();
// Dès le niveau 1
$config->setMinDownloadLevel(1);

// La coloration des fichier Asymptote .asy est la même que celle du C.
$extEnscript[".asy"] = "c";

// Parce que c'est obligatoire ;-)
set_time_limit(0);

// Number of spaces to expand tabs to in diff/listing view across all repositories
$config->expandTabsBy(8);
?>
  

Le prolème de cette configuration est le suivant :

  • en l'état actuel, vous ne verrez jamais aparaître les dépôts pour lesquels il faut être authentifié ; typiquement tous les dépôts de private pour lesquels vous aurez pris soin de désigner un groupe d'utilisateurs autorisés à lire le dépôt.
  • si on met en place le système d'authentification d'Apache dans <Directory /usr/share/websvn> il faudra s'identifier même pour accéder aux dépôts publics.

Pour ces raisons, j'ai décidé d'implémenter dans WebSVN mon propre système d'authetification. C'est bien ce que d'aucun appelleront un hack mais si quelqu'un a mieux à proposer surtout qu'il n'hésite pas à le partager...

Configuration « à la PIPRIME »

En conservant la configuration d'Apache que j'ai donné précédemment, on va modifier le gabarit et la configuration de WebSVN pour implémenter une authentification en PHP en essayant de modifier le moins possible le comportement original de WebSVN.
Le but, rappelons le, est d'avoir, sur la même instance/page de WebSVN, les projets publics accessibles sans authentification et les projets privés accessibles après authentification.

Commençons par le gabarit/template de WebSVN ; on y introduit le nouveau tag [websvn:CONNECTMESSAGE]. Je vous laisse adapter mon gabarit /usr/share/websvn/templates/calm/index.tmpl à votre goût mais il faut y trouver le tag en question.

<div id="select">
     [websvn:lang_form]
     <div>
        [websvn:lang_select]<span class="submit">[websvn:lang_submit]</span>
     </div>[websvn:lang_endform]
</div>

<h6 style='color:gray;text-align:left;font-weight:normal;line-height:1.7em;border-bottom:1px solid gray;padding:10px 5px 10px 5px;margin:-1.7em 2% 1em 2%;'>[websvn:CONNECTMESSAGE]</h6>

<div id="info">
<h2>À propos</h2>
Projets développés et hébergés sur <a href="http://www.piprime.fr">PIPRIME.FR</a>.<br />
Identifier vous pour accéder aux projets privés<br />
<i>(user=demo sans mot de passe pour tester cette fonctionalité)</i>.
<dl>
  <dd>En savoir plus sur Subversion à <a href="http://subversion.tigris.org">subversion.tigris.org</a>.</dd>
</dl>
</div>

<div id="wrap">
<h2 class="regular">[lang:PROJECTS]</h2>

[websvn-test:flatview]
    <dl class="projectlist">
      <dt>[lang:PROJECTS]:</dt>
      [websvn-startlisting]
      <dd>[websvn:projlink]</dd>
      [websvn-endlisting]
    </dl>
[websvn-else]
    <div class="projectlisthead">[lang:PROJECTS]:</div>
	<div class="projectlist">
	[websvn-startlisting]
	[websvn-test:isprojlink]
		<div class="project">[websvn:listitem]</div>
	[websvn-else]
		[websvn:listitem]
	[websvn-endtest]
	[websvn-endlisting]
	</div>
[websvn-endtest]
</div>
  

On crée ensuite un fichier, disons pi-auth.php, accessible à Apache (par exemple dans /usr/share/websvn/) et qui permet de s'authentifier, de se déconnecter et d'initialiser principalement la variable de session $_SESSION['REMOTE_USER']. Attention, Il faut configurer dans ce fichier le tableau $users qui contient les noms des utilisateurs et leur mot de passe correspondant.

<?php
session_start();

//utilisateur => mot de passe
$users = array('USER1' => 'PASS1',
               'USER2' => 'PASS2');

if(!isset($_SESSION['HTTP_REFERER'])) {
  $_SESSION['HTTP_REFERER'] = $_SERVER['HTTP_REFERER'];
}

# Come back
function comeback($ref) {
  header('Location: '.$ref);
  exit;
}

function reset_session() {
  $ref=$_SESSION['HTTP_REFERER'];
  $_SESSION = array();
  unset($_COOKIE[session_name()]);
  session_destroy();
  session_start();
  $_SESSION['HTTP_REFERER']=$ref;
}

function retry_message() {
  echo '<h2>Vous n\'êtes pas autorisé à continuer...</h2>';
  echo '[<a href=\'' . $_SERVER['PHP_SELF'] . '\'>S\'identifier à nouveau</a>]';
}

# LOGOUT
if (isset($_GET['logout']) && isset($_SESSION['REMOTE_USER'])) {
  $ref=$_SESSION['HTTP_REFERER'];
  reset_session();
  comeback($ref);
}

if (!isset($_SERVER['PHP_AUTH_USER']) ||
    (!isset($_SESSION['REMOTE_USER']) && !$_SESSION['login']))
  {
    header("WWW-Authenticate: Basic realm=\"Authentification WebSVN PIPRIME.FR\"");
    header("HTTP/1.0 401 Unauthorized");
    $_SESSION['login']=true;
    retry_message();
    exit;
  } else {
  $httpd_username = filter_var($_SERVER['PHP_AUTH_USER'],
                               FILTER_SANITIZE_STRING,
                               FILTER_FLAG_ENCODE_HIGH|FILTER_FLAG_ENCODE_LOW);
  $httpd_password = filter_var($_SERVER['PHP_AUTH_PW'], FILTER_SANITIZE_STRING,
                               FILTER_FLAG_ENCODE_HIGH|FILTER_FLAG_ENCODE_LOW);
  if(isset($users["$httpd_username"]) &&
     $httpd_password == $users["$httpd_username"])
    {
      // utilisateur et mot de passe valide
      $_SESSION['REMOTE_USER'] = $httpd_username;
      comeback($_SESSION['HTTP_REFERER']);
    }
  else
    {
      reset_session();
      echo '<h1>Mauvaise Identité</h1>';
      retry_message();
    }
}

?>
  

Il ne reste plus qu'à définir la valeur du fameux tag [websvn:CONNECTMESSAGE] et positionner la valeur de la variable d'environnement $_SERVER['REMOTE_USER'] sur $_SESSION['REMOTE_USER'] dans le fichier de configuration de WebSVN /etc/websvn/config.php.
Pour se faire, il suffit d'insérer au début du fichier config.php donné précédemment le code PHP suivant :

session_start();

if(isset($_SESSION['REMOTE_USER'])) {
  $_SERVER['REMOTE_USER']=$_SESSION['REMOTE_USER'];
  $vars['CONNECTMESSAGE'] = '<a href=\'pi-auth.php?logout\'>Se déconnecter</a>';
} else {
  $vars['CONNECTMESSAGE'] = '<a href=\'pi-auth.php?login\'>S\'identifier</a>';
}
  

Ne pas oublier de faire correspondre les noms d'utilisateurs de l''authentification de pi-auth.php avec ceux du fichier Quelque_Part/svn/repos/authz
Vous devez obtenir le comportement de ma page de projets sous contrôle SVN pour laquelle j'ai créé un projet privé fictif accessible seulement à l'utilisateur demo, sans mot de passe ; je vous laisse tester...

Netographie

Étiquettes : , ,