Déployer un site web via sftp et Gitlab

tbowan

10 juin 2019

Avec notre nouvel hébergement, nous avons du revoir nos scripts de déploiement continu. Remplacer rsync par lftp n'est pas compliqué mais nécessite de fournir les données d'authentifications à l'environnement de déploiement. Heureusement, gitlab fournit une méthode sécurisée pour protéger ces informations sensibles.

Depuis que nous sommes passés chez un hébergeur, nous avons du revoir notre déploiement continu. Avant, nous utilisions un exécuteur Gitlab directement sur le serveur, qui se chargeait de copier le site compilé dans le bon répertoire. Maintenant, nous devons faire cette copie à distance.

Copier avec lftp

Instinctivement, on avait pensé à rsync. C’est l’outil par excellence lorsqu’on veut synchroniser des répertoires à travers le réseau de manière compressée et sécurisée. Malheureusement pour nous, notre offre d’hébergement n’accepte pas les transferts via ssh, uniquement via (s)ftp.

On s’est donc tourné vers la commande lftp qui lui est proche. Grâce à sa commande mirror il est assez simple de synchroniser deux répertoires via le réseau comme le montre la commande suivante :

lftp sftp://$NOM_HOTE -e "mirror -e -R $SOURCE $DESTINATION ; quit"

Qui synchronisera le répertoire local $SOURCE vers le répertoire distant $DESTINATION grâce à l’option -R1.L’option -e supprimant les fichiers qui n’existent plus.

Identifiants du client

C'est la partie la plus facile car notre hébergeur utilise une authentification par login et mot de passe. Ceux-ci sont fournis directement via l’interface web de gestion de l’hébergement et nous n’avons qu’à les recopier.

Pour les passer à lftp, nous pourrions utiliser l’option -p mais nous avons préféré les passer directement dans l’URL du serveur :

lftp sftp://$IDENTIFIANT:$MOT_DE_PASSE@$NOM_HOTE \
    -e "mirror -e -R $SOURCE $DESTINATION ; quit"

Empreinte du Serveur

Pour authentifier le serveur, lftp utilise les mêmes mécanismes que ssh. À savoir, enregistrer les empreintes cryptographiques (i.e. la clé publique) des hôtes déjà connus dans le fichier ~/.ssh/knownhosts.

Sur notre propre machine, il suffirait de tenter une connexion ssh sur le serveur, accepter sa clé publique puis quitter (pas besoin de se connecter) pour mettre ce fichier à jour. On pourrait ensuite utiliser lftp directement. Comme on utilise des conteneur docker, on va devoir intégrer cette empreinte manuellement à l'environnement.

Pour récupérer cette empreinte, plutôt que de tenter de copier votre fichier2, le plus pratique est encore d’utiliser ssh-keyscan qui est justement fait pour ça : récupérer les empreintes des serveurs pour les insérer ensuite dans les fichiers known_host. La page de manuel stipule qu’il peut récupérer plus de 1000 empreintes en quelques dizaines de secondes, largement suffisant pour notre cas :wink: :

$ ssh-keyscan ftp.cluster021.hosting.ovh.net
# ftp.cluster021.hosting.ovh.net:22 SSH-2.0-OpenSSH_6.7p1
ftp.cluster021.hosting.ovh.net ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCzSgCrXcu9rQrvccfe35g/Adthn8v451gCBpytR1y/3hQPs2fNvvmS0ivUiOlTNlLwEqiIaKJxYm6aTc9M9JgoWb5gYb57MjBKJ1R0I245bx40zDj+hHailq0Sy40eXC0K+WScRSu9eyrSpptp0XiA4tM3FazJN4XqGGnPzxqwNwsquyfqXSp9qC8M9BhM7dOgHXo7WCJ9VJ94ypKprsNfI2tv/aMP7BUi0fQGi4LiK9LyqIcbhYf1pm8kXVe/2Hr1OC5h6LZJkHhOnmMkZjK21LsIW+kYBSl2yw+RieWnl0DUguxdaa4K51YOWbxFXY29wWudX/4T8brCL0uxp4mr
# ftp.cluster021.hosting.ovh.net:22 SSH-2.0-OpenSSH_6.7p1
# ftp.cluster021.hosting.ovh.net:22 SSH-2.0-OpenSSH_6.7p1
ftp.cluster021.hosting.ovh.net ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJSgweEQn216+sUrv4A0HwrnvJi03tu/LPeJ176yB29a

Qui nous trouve deux clés, l’une en RSA, l’autre en courbes elliptiques. Ces lignes seront donc ajoutées au fichier known_host lors du build.

Utilisation des Variables Gitlab

Maintenant que nous disposons des informations d’authentification, il faut encore les passer au scripts de déploiement. Les stocker directement dans le dépôt génère des risques inutiles : lecture par des contributeurs qui n’ont pas à connaître ces informations, publication de ces fichiers sur le web3, …

La solution de gitlab est d’utiliser des variables d’environnement. Il s’agit de variables disponibles aux scripts de déploiement, stockées et gérées par gitlab en dehors du dépôt. Ces variables peuvent être masquées (pour ne pas apparaître dans les journaux des builds) et protégées (pour n’être disponible que sur les branches protégées également).

Leur configuration se fait via la page du projet, dans le menu Settings /CI/CD, puis dans la partie Variables qu’il faut ouvrir. Dans notre cas, nous avons défini trois variables et un fichier4.

Variables d’environnement de déploiement

Variables d’environnement de déploiement

On pourrait ne stocker en variable que les données vraiment sensibles (comme le mot de passe, voir l’identifiant) mais je préfère y stocker toutes les informations liées au déploiement car en cas de changement de ces paramètres, aucun commit sur le dépôt ne sera nécessaire, un simple rebuild suffira.

Configuration yaml

Il ne reste finalement qu’à tout regrouper dans le fichier de configuration .gitlab-ci.yaml dont voici la tâche correspondante (avec la création du fichier known_host et synchronisation du contenu).

deploy-master:
    stage: deploy
    only:
    - master
    script:
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - cat $KNOWN_HOST > ~/.ssh/known_hosts
    - chmod 644 ~/.ssh/known_hosts
    - lftp sftp://$IDENTIFIANT:$MOT_DE_PASSE@$NOM_HOTE -e "mirror -e -R public ~/www; quit"

Et Après ?

Site statique
27 Septembre 2017 - tbowan. Après avoir expérimenté le PHP, nous revenons aux sources avec une génération statique du site des arsouyes. Pour les curieux, voici comment nous nous y sommes pris.

  1. Sans l’option -R, c’est l’inverse. Le répertoire source est considéré comme sur l’hôte distant et la destination est considérée comme locale. lftp est en fait prévu pour, par défaut, rapatrier du contenu depuis des serveurs.

  2. Indice : ça ne marche pas.

  3. Pratique de développement très mauvaise, mais qu’on rencontre parfois, i.e. avec les identifiants Azure.

  4. Lorsque le contenu est un peu lourd, il peut être utile de le mettre dans un fichier. La variable correspondante contient alors le nom du fichier temporaire qu’on peut alors lire ou copier.