Créer un dépot Debian et y mettre ses propres paquets

Dans cet article, on va apprendre à créer un dépot Debian. Mais pas un dépot qui va se contenter de mirroir les dépots officiels Debian, mais un dépot où l'on va mettre ses propres paquets dessus.

Côté serveur

Avant de créer les paquets, on va tout d'abord setup le dépot. Il existe des tas d'outils pour créer des dépots, j'ai personnellement choisi reprepro. Et pour accéder au dépot, on ne va pas utiliser un accès HTTP ou FTP comme d'habitude, mais passer par SSH (oui, c'est possible). On n'a besoin d'aucun démon à installer pour créer ce type de dépot.

Côté serveur, on va mettre tout ça dans le home d'un user créé pour l'occasion.

adduser myrepo

Rien de bien complexe jusqu'ici.
On va générer ensuite une clé GPG

$ su - myrepo
$ pwd
/home/myrepo
$ gpg --gen-key

Suivez l'assistant.

On créé ensuite deux répertoires, conf et incoming

$ mkdir -p {conf,incoming}

Ensuite, on va créer le fichier conf/distribution qui contiendra la configuration du dépot. En l'occurence, mon dépot contiendra quatres distributions. Une distribution, c'est on ensemble de paquets à une version donnée. Dans Debian, une distribution, c'est Wheezy, Jessie...

$ cat conf/distributions 
Origin: Damien Gustave
Label: MYREPO
Suite: stable
Codename: myrepo-stable
Architectures: amd64
Components: main
Description: My wonderfull repo
SignWith: yes

Origin: Damien Gustave
Label: MYREPO
Suite: stable-integration
Codename: myrepo-stable-integration
Architectures: amd64
Components: main
Description: My wonderfull repo
SignWith: yes

Origin: Damien Gustave
Label: MYREPO
Suite: testing
Codename: myrepo-testing
Architectures: amd64
Components: main
Description: My wonderfull repo
SignWith: yes

Origin: Damien Gustave
Label: MYREPO
Suite: testing-integration
Codename: myrepo-testing-integration
Architectures: amd64
Components: main
Description: My wonderfull repo
SignWith: yes

J'ai donc quatres dépots

  • stable
  • stable-integration
  • testing
  • testing-integration

Stable et testing sont assez simple à comprendre, c'est exactement la même chose qu'un stable et testing sous Debian (Jessie/Stretch à l'heure où je vous parle). En revanche, pour les -integration, c'est un peu moins évident. Ces dépots vont en fait me servir à faire mes tests de création de paquets sans impacter ce que j'ai déjà en prod.

Par exemple, je vais créer un paquet P1 à une version v1. Je l'ajoute à mon dépot testing-integration. Je prends une VM qui à comme source le dépot testing-integration, et j'apt-get pour upgrader mon paquet. Mais je me rends compte que j'ai fait une erreur dans un script, qu'un fichier n'est pas bon, bref, n'importe quoi qui fait que finalement ce paquet n'est pas correct. Du coup, je peux faire mes tests tranquillement sur cette distribution sans qu'une autre machine branchée sur ce repo ne viennent upgrader mon paquet tout foireux. Je peux continuer à itérer mes releases en passant mon paquet P1 à la v2, puis quand je déciderai qu'il est stable, je pourrais le pousser sur mon testing.

Pour ajouter un paquet (on va récupérer n'importe quel .deb sur internet pour tester), il faut d'abord le mettre dans le dossier incoming, puis le signer avec notre clé GPG générée plus haut.

$ dpkg-sig --sign builder incoming/testpaquet.deb

Une fois un premier paquet signés, tous les autres devraient automatiquement être signés à l'ajout sur le dépot.

Il faut maintenant inclure le paquet sur notre dépot :

$ reprepro -Vb . includedeb testing-integration incoming/testpaquet.deb
incoming/testpaquet.deb: component guessed as 'main'
Created directory "./pool/main/t/testpaquet" 
Exporting indices...
Successfully created './dists/testing-integration/Release.gpg.new'
Successfully created './dists/testing-integration/InRelease.new'

On voit ici qu'on précise la distribution dans laquelle on souhaite que le paquet apparaisse. Il ne sera donc pas présent dans les 3 autres, il faudra l'ajouter explicitement si on le veut.

Voilà, notre premier paquet est disponible à l'installation !

Côté client

On accède au dépot en SSH, donc pour que ce soit transparent, on va ajouter notre clé SSH au serveur. Il faut cependant que ce soit la clé de l'utilisateur root (c'est logique puisque c'est par lui qu'on passe, via sudo ou non, pour installer un paquet).

# whoami
root
# ssh-copy-id myrepo@myreposerver.com

Editer le fichier /etc/apt/sources.list pour ajouter le dépot

# echo "deb ssh://myrepo@myreposerver.com:/home/myrepo/ myrepo-testing-integration main" >> /etc/apt/sources.list
# aptitude update

On peut maintenant installer un paquet du dépot. S'il a des dépendances, il ira les chercher tout seul dans tous les dépots configurés sur la machine cliente.

$ sudo aptitude install testpaquet

Félicitations, vous avez désormais un dépot rien qu'à vous !

Créer un paquet

Nous allons utiliser l'outil de base de Debian pour créer les paquets, dpkg-deb.

Pour créer un paquet, on va commencer par créer un répertoire correspondant au nom du paquet.

$ mkdir monPremierPaquet
$ cd monPremierPaquet

On va mettre dans ce répertoire tous les dossiers et fichiers dont à besoin notre paquet, comme si l'on était à la racine / du système.

Puis on créé un répertoire DEBIAN (en majuscule), qui est un répertoire spécial permettant de décire le paquet

$ mkdir DEBIAN

Dans ce répertoire, on va créer un fichier control qui servira à décrire ce paquet.

Ce fichier pourra prendre cette forme :

Package: monPremierPaquet
Version: 0.1
Section: main
Priority: optional
Architecture: all
Depends: python3 (> 3.4), rsync, sudo, htop, openntpd, exim4, php5 (>= 5.3.0), php5 (< 5.4.0)
Maintainer: Damien Gustave <damien.gustave@gmail.com>
Description: Fourni mon premier paquet
  • Package: C'est le nom du paquet sur le dépot
  • Version: Version du paquet que l'on construit
  • Section: main, contrib, non-free etc... Ca vous parle.
  • Priority: optional / required...
  • Architecture: Définit sur quelle(s) architecture(s) le paquet peut s'installer (all, amd64, i386...)
  • Depends: Définit la liste des dépendances du paquets, ainsi que leur versions (si nécessaire).
  • Maintainer: Vous
  • Description: Un champ texte pour décrire le contenu/rôle du paquet.

On peut trouver d'autres champs dont la description se trouve sur la documentation officielle.

Un autre fichier important dans le dossier DEBIAN est le fichier postinst. Facultatif, il sera pourtant très utile. En effet, c'est un script qui est appelé après extraction des fichier lors de l'installation d'un paquet. C'est un script standard, vous pouvez donc l'écrire dans le langage que vous voulez du moment que vous précisez le shebang adéquat. Le plus simple reste pourtant de faire du bash.

Voici un template de ce fichier :

$ cat postinst
#!/bin/bash
# postinst script
#
# see: dh_installdeb(1)

set -e

# summary of how this script can be called:
#        * <postinst> `configure' <most-recently-configured-version>
#        * <old-postinst> `abort-upgrade' <new version>
#        * <conflictor's-postinst> `abort-remove' `in-favour' <package>
#          <new-version>
#        * <postinst> `abort-remove'
#        * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
#          <failed-install-package> <version> `removing'
#          <conflicting-package> <version>
# for details, see http://www.debian.org/doc/debian-policy/ or
# the debian-policy package


case "$1" in
    configure)
		#!/bin/bash
		echo "Configuring mon premier paquet..."

		mkdir -p *****
		chown root:adm *******

    ;;

    abort-upgrade|abort-remove|abort-deconfigure)
		# Nothing to do when aborting upgrade
    ;;

    *)
        echo "postinst called with unknown argument \`$1'" >&2
        exit 1
    ;;
esac

# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.

#DEBHELPER#

exit 0

De la même manière, il existe un fichier preinst (avant que les fichiers soit extrait lors de l'install ou l'upgrade), prerm et postrm.

Le script est appelé avec le type d'installation (cf exemple au dessus) et nom de la version en argument. Ce qui permet de faire des trucs relativement précis et efficaces. Cette page décrit comment s'enchainent les scripts lors de l'apt-get.

Maintenant, nous allons construire le paquet. Placez-vous dans votre home, au dessus de la racine du paquet.

$ ls -l monPremierPaquet
total 16
drwxrwxr-x  2 damien damien 4096 oct.   8 17:47 DEBIAN
drwxrwxr-x  3 damien damien 4096 août  19 10:19 etc
drwxrwxr-x  2 damien damien 4096 août  19 10:19 root
drwxrwxr-x  3 damien damien 4096 sept.  4 16:24 usr

Puis, lancer la commande :

dpkg-deb --build monPremierPaquet

Cela va créer un .deb (monPremierPaquet.deb) que vous pourrez ajouter au dépot via reprepro (voir plus haut).

Si vous voulez que debconf vous pose des questions lors de l'installation d'un paquet (par exemple, le choix d'un mot de passe), il vous faut définir des fichiers supplémentaires.

Créer le fichier DEBIAN/templates.

$ cat monPremierPaquet/DEBIAN/templates
Template: monPremierPaquet/monPassword
Type: password
Default: passssssssssword!
Description: Please enter my password

Puis, créer le fichier DEBIAN/config, qui servira à poser les questions.

$ cat monPremierPaquet/DEBIAN/config
#!/bin/bash
set -e

# Source debconf library.
. /usr/share/debconf/confmodule

db_input critical monPremierPaquet/monPassword || true
db_go

On peut désormais s'en servir dans le fichier postinst, par exemple.

# Source debconf library.
. /usr/share/debconf/confmodule

db_get monPremierPaquet/monPassword
# ANSWER IS STORED IN $RET VARIABLE
echo $RET

Deux points important :

  • Il ne faut pas faire écrire le script config, cela fera planter l'install
  • Si l'install d'un paquet se freeze, c'est peut être car vous avez voulu redémarrer un service dans le script postinst. Ca m'est arrivant en redémarrant apache, les descripteurs de fichiers sont rester attachés au process dpkg. J'ai forcé le détachage avec un truc très moche : service apache2 restart > /dev/null 2>&1 3>&1 0>&1

D'ailleurs, en parlant de ça, il faut éviter d'utiliser la syntaxe service xxxx restart, il faut plutôt utiliser ìnvoke-rc.d xxxx restart. La différence, c'est que invoke-rc.d va faire appel au script /usr/sbin/policy-rc.d, qui permet d'autoriser ou non un service à redémarrer.

Sources (entre autres) :

Damien Gustave

Read more posts by this author.