TP ToIP + QoS - Réseaux et qualités de service

Théophile AVENEL

Introduction

Ce travail pratique porte sur la mise en place d'une infrastructure de Téléphonie sur IP (ToIP) avec Asterisk, intégrée dans des conteneurs LXD, et l'implémentation de mécanismes de Qualité de Service (QoS).

Prérequis : Ce TP nécessite une machine virtuelle Ubuntu, VirtualBox, et une compréhension de base des réseaux et des protocoles SIP.

Les objectifs de ce TP sont :

  • Configuration d'un environnement de conteneurs LXD avec stockage Btrfs
  • Installation et configuration d'Asterisk comme IPBX
  • Compilation et intégration de clients SIP (PJSIP)
  • Tests de signalisation et d'établissement d'appels
  • Implémentation de mécanismes de QoS pour la ToIP

Partie I : Configuration de conteneurs LXD

1.1 Espace de stockage des systèmes de fichiers

Nous préparons un disque dédié pour héberger le backend Btrfs de LXD. Cette isolation évite de saturer le disque système et suit la démarche décrite dans les Figures 5 à 7 du rapport.

Remarque : ce TP est réalisé dans une VM Ubuntu exécutée sous VirtualBox. Avant de manipuler la couche de stockage, éteignez la VM afin d’éviter toute corruption du disque virtuel.

Nous ajoutons un VDI distinct du disque système pour y loger exclusivement les volumes LXD. Cette séparation nous laisse reformater ou redimensionner l’espace réservé aux conteneurs sans toucher au système invité, facilite les snapshots Btrfs et protège la racine / contre une saturation accidentelle.

Ajout d'un disque virtuel dédié dans VirtualBox
Figure 1 : Ajout d'un second disque virtuel (VDI) dédié à LXD dans VirtualBox.
Paramétrage du disque VDI de 40 Gio
Figure 2 : Création d'un disque VDI de 40 Gio en allocation dynamique.

Le choix du format VDI (format natif VirtualBox) garantit la meilleure compatibilité avec les fonctions de l’hyperviseur : snapshots cohérents, clonage rapide et possibilité de compactage ou de redimensionnement via VBoxManage. Avec l’allocation dynamique, l’image ne grossit que selon les blocs réellement écrits, ce qui économise l’espace disque de l’hôte tout en restant transparent pour le noyau invité. Si vous devez redimensionner le pool LXD plus tard, vous pourrez utiliser VBoxManage modifymedium --resize pour agrandir le VDI.

Une fois le disque ajouté et attaché à la VM, relancez Ubuntu et poursuivez avec le partitionnement LVM décrit ci-dessous.

  1. Ajouter un second disque virtuel (VDI) dans VirtualBox, de préférence 40 Gio avec allocation dynamique.
  2. Initialiser le disque avec une table GPT et une unique partition LVM.
  3. Chaîner LVM : création du volume physique (pvcreate), du groupe (vgcreate) puis du volume logique de 32 Gio (lvcreate).
  4. Formater le volume logique en Btrfs avec l’étiquette lxd et conserver l’UUID pour le montage automatique.
Prudence : les commandes parted et pvcreate sont destructrices. Vérifiez deux fois l’identifiant du disque (/dev/sdb dans l’exemple) avant d’exécuter le script.
Bash
# Vérification du disque
                            
                    

Vérifier le disque et poser les variables

Redémarrez la VM après l’ajout du VDI puis lancez lsblk pour vérifier que le disque apparaît bien en /dev/sdb, distinct du disque système /dev/sda. Les entrées loop* correspondent aux snaps Ubuntu et sr0 au média des Guest Additions.

Installez ensuite les prérequis (lvm2, btrfs-progs, parted) puis posez les variables de travail pour standardiser la suite des commandes.

Résultat de lsblk montrant /dev/sdb
Figure 3 : Vérification du nouveau disque via lsblk.
Bash
# Inventaire et installation des prérequis
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT
sudo apt install -y lvm2 btrfs-progs parted
                    

# Définition des variables
DISK=/dev/sdb
VG=vg_lxd
LV=lxd
SIZE=32G
MNT=/var/lib/lxd

Nous préparons le nouveau disque pour LVM en trois commandes parted. Nous posons d’abord une table de partitions GPT afin de repartir proprement et de bénéficier d’un schéma moderne, puis nous créons une unique partition couvrant tout le disque (1 MiB ? 100 %) et enfin nous activons le flag LVM. Cette séquence (Figure 5) garantit un alignement correct des blocs et assure que lvm2 reconnaîtra immédiatement ${DISK}1 comme support valide. Remarque : ces opérations peuvent aussi être réalisées via l’interface graphique de gparted.

Partitionnement GPT du disque dédié
Figure 5 : Partitionnement du disque en vue d’un usage LVM.

Cette étape est destructrice pour le disque ciblé : elle efface toute structure préexistante sur $DISK. D’où l’importance d’avoir confirmé juste avant, avec lsblk, que $DISK pointe bien vers le nouveau volume (/dev/sdb). À l’issue des commandes parted, /dev/sdb1 est prêt pour la suite de la chaîne PV ? VG ? LV.

Nous enchaînons alors les trois étapes classiques de LVM (Figure 6). pvcreate ${DISK}1 initialise la partition comme volume physique, vgcreate agrège ce PV dans le groupe vg_lxd et lvcreate alloue un volume logique lxd de 32 Gio. Les messages « successfully created » confirment la réussite de chaque étape et la disponibilité du LV pour la suite.

Chaînage pvcreate vgcreate lvcreate
Figure 6 : Chaînage LVM (pvcreate, vgcreate, lvcreate).

Nous formatons enfin le volume logique vg_lxd/lxd en Btrfs avec l’étiquette lxd (Figure 7). La sortie de mkfs.btrfs indique un système de fichiers de 32 Gio, un UUID unique et les profils par défaut (Data=single, Metadata=DUP) qui apportent de la redondance pour les métadonnées. Les fonctionnalités modernes (free-space-tree, no-holes, skinny-metadata) sont activées automatiquement et adaptées aux noyaux récents.

Formatage Btrfs du volume logique
Figure 7 : Création du système de fichiers Btrfs sur le LV lxd.
Pourquoi ces variables ? Elles évitent les erreurs de frappe et permettent de rejouer facilement la préparation du stockage : DISK cible le nouveau VDI, VG et LV nomment le groupe et le volume logique, SIZE fixe la capacité demandée par le TP et MNT pointe vers le chemin LXD (issu de l’installation snap).
Définition des variables shell pour LXD
Figure 4 : Déclaration des variables shell pour automatiser la préparation du stockage LXD.

Partitionnement, LVM et formatage

Avec ces variables en place, enchaînez le schéma PV ? VG ? LV (Figures 5 et 6) avant de formater en Btrfs (Figure 7).

Bash
# Partitionnement GPT
sudo parted "$DISK" --script mklabel gpt
sudo parted "$DISK" --script mkpart primary 1MiB 100%
sudo parted "$DISK" --script set 1 lvm on

# Chaînage LVM
sudo pvcreate ${DISK}1
sudo vgcreate "$VG" ${DISK}1
sudo lvcreate -L "$SIZE" -n "$LV" "$VG"

# Formatage Btrfs
sudo mkfs.btrfs -L lxd /dev/mapper/${VG}-${LV}

La Figure 9 illustre l’ajout de l’entrée persistante dans /etc/fstab. L’usage de l’UUID garantit un montage stable au démarrage, indépendamment des noms /dev/sdX. Le test grep -qF rend l’opération idempotente et évite les doublons, tandis que les champs finaux 0 2 demandent à systemd d’effectuer un fsck après la racine.

Ajout de l'entrée fstab
Figure 9 : Ajout idempotent de l’entrée fstab via l’UUID.

Une fois mount -a exécuté, contrôlez le résultat comme dans la Figure 10 : lsblk -f, mount, df -h et btrfs filesystem df doivent confirmer la présence du volume vg_lxd-lxd monté en Btrfs sur $MNT avec l’étiquette lxd. Les options rw,relatime,space_cache=v2,subvol=/ montrent que le backend est prêt pour LXD.

Vérifications de montage Btrfs
Figure 10 : Contrôle du montage Btrfs et de l’espace disponible.
Sortie attendue
LSBLK OUTPUT (extrait)
sdb         40G disk
+-sdb1      40G part

pvcreate /dev/sdb1   -->  Physical volume "/dev/sdb1" successfully created
vgcreate vg_lxd ...  -->  Volume group "vg_lxd" successfully created
lvcreate ... -n lxd  -->  Logical volume "lxd" created
mkfs.btrfs ...       -->  UUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

Après ces vérifications, le volume Btrfs de 32 Gio est monté et persistant grâce à l’entrée fstab : même si systemd signale d’exécuter daemon-reload, le montage est effectif. Nous pouvons désormais initialiser LXD en choisissant btrfs comme backend et en pointant sur ce chemin pour y stocker images, conteneurs et snapshots.

1.2 Installation et configuration de LXD

Nous montons le volume Btrfs nouvellement formaté et initialisons LXD en le pointant vers ce stockage. Cette séquence est illustrée par les Figures 8 à 14 du rapport.

Monter le volume Btrfs

Avant de rendre le montage persistant, créez le point de montage (Figure 8). Le mkdir -p est idempotent : si le répertoire existe déjà, la commande ne produira pas d’erreur. Dans notre TP, $MNT pointe vers le chemin snap /var/snap/lxd/common/lxd (sur une ancienne installation .deb, on utiliserait /var/lib/lxd).

Bash
sudo mkdir -p "$MNT"
Création du répertoire de montage
Figure 8 : Création du point de montage dédié à LXD.
Création du répertoire de montage
Figure 9 : Ajout d’une entrée persistante dans /etc/fstab via l’UUID (avec garde anti-doublon).

Ajoutez l’entrée correspondante dans /etc/fstab, montez le volume puis vérifiez le résultat avec lsblk -f et mount.

Bash
UUID=$(sudo blkid -s UUID -o value /dev/mapper/${VG}-${LV})
FSTAB_LINE="UUID=$UUID $MNT btrfs defaults,relatime 0 2"

grep -qF "$FSTAB_LINE" /etc/fstab || echo "$FSTAB_LINE" | sudo tee -a /etc/fstab

                        
Création du répertoire de montage
Figure 10 : Montage du Btrfs et vérifications finales du point de montage LXD.
Bash


sudo mount -a
lsblk -f | grep "$VG"
mount | grep "$MNT"
df -h "$MNT"
sudo btrfs filesystem df "$MNT"
Sortie attendue
NAME        FSTYPE LABEL UUID                                 MOUNTPOINT
+-vg_lxd-lxd btrfs lxd   xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx  /var/lib/lxd
UUID=... /var/lib/lxd type btrfs (rw,relatime,space_cache=v2,subvol=/)

Installer et initialiser LXD

Sur Ubuntu 24.04, l’installation apt peut échouer. Le rapport conseille l’usage de snap, suivi de l’ajout de l’utilisateur courant au groupe lxd puis de l’exécution de lxd init en mode interactif.

Création du répertoire de montage
Figure 11 : Bascule du point de montage LXD vers /var/lib/lxd et vérifications.
Création du répertoire de montage
Figure 12 : Installation de LXD via snap, vérification du montage et appartenance au groupe lxd.
Bash
sudo snap install lxd
sudo usermod -aG lxd "$USER"
newgrp lxd

sudo lxd init <<'EOF'
no
no
btrfs
/var/lib/lxd
no
no
no
no
EOF

Lors de l’assistant, sélectionnez :

  • Clustering : no
  • Storage backend : btrfs (option « existing pool » en pointant sur /var/lib/lxd)
  • Network bridge : laissez LXD créer lxdbr0
  • MAAS / API : no (non requis pour ce TP)

Vérifications post-init

Les Figures 13 et 14 montrent les commandes de validation. Assurez-vous que le pool default pointe bien sur /var/lib/lxd et que les sous-volumes Btrfs sont créés.

Bash
lxc version
lxc storage list
lxc storage show default
sudo btrfs subvolume list $MNT | head
Sortie attendue
+---------+-------------+--------+---------------------------+
| NAME    | DRIVER      | SOURCE | DESCRIPTION               |
+---------+-------------+--------+---------------------------+
| default | btrfs       | /var/lib/lxd | default storage pool |
+---------+-------------+--------+---------------------------+
ID 257 gen ... top level 5 path containers
ID 258 gen ... top level 5 path images
À retenir : une fois ces vérifications réalisées, LXD est prêt à créer des conteneurs en s’appuyant sur le volume Btrfs dédié et sur le bridge lxdbr0 généré automatiquement.

1.3 Création d'un conteneur pour Asterisk

Nous déployons le conteneur asterisk01 depuis l’image officielle ubuntu:20.04. Les Figures 16 à 20 détaillent la séquence : création du conteneur, vérifications réseau, installation des paquets et préparation de l’accès SSH.

  1. Lancer le conteneur avec lxc launch puis vérifier qu’il est bien RUNNING et connecté au bridge lxdbr0.
  2. Mettre à jour l’OS invité et installer openssh-server, sudo ainsi que les paquets asterisk/asterisk-mp3.
  3. Configurer le service SSH pour accepter l’authentification par clé et par mot de passe (Figure 18).
  4. Créer un compte applicatif (theophile dans l’exemple) et tester les services clés (Figure 20).
Bash
# 1. Création et état du conteneur (Figure 16)
lxc launch ubuntu:20.04 asterisk01
lxc list asterisk01
lxc info asterisk01
lxc exec asterisk01 -- ip -br addr show eth0

# 2. Mise à jour et installation d'Asterisk (Figure 17)
lxc exec asterisk01 -- apt update
lxc exec asterisk01 -- apt install -y openssh-server sudo asterisk asterisk-mp3
Sortie attendue
+------------+---------+------+------+-----------+-----------+
| NAME       | STATE   | IPV4 | ...
| asterisk01 | RUNNING | 10.142.96.x (eth0) | ...
+------------+---------+------+------+-----------+-----------+
...
Setting up asterisk ...
Processing triggers for ufw ...

Configurer SSH et l’utilisateur d’exploitation

Le service SSH intégré aux images cloud impose parfois des overrides sous /etc/ssh/sshd_config.d. Réactivez l’authentification par clé et par mot de passe, puis redémarrez sshd (Figure 18). Créez enfin l’utilisateur applicatif (theophile) comme sur la Figure 19.

Bash
# 3. Politique SSH
lxc exec asterisk01 -- sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/' /etc/ssh/sshd_config
lxc exec asterisk01 -- sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config
lxc exec asterisk01 -- sed -i 's/#PermitEmptyPasswords no/PermitEmptyPasswords no/' /etc/ssh/sshd_config
lxc exec asterisk01 -- systemctl restart ssh
lxc exec asterisk01 -- sshd -T | egrep 'authentication'

# 4. Compte d'exploitation (idempotent)
lxc exec asterisk01 -- bash -lc "id -u theophile || useradd -m -s /bin/bash theophile"
lxc exec asterisk01 -- usermod -aG sudo theophile
lxc exec asterisk01 -- bash -lc "echo 'theophile:motdepasse' | chpasswd"
Tip : après avoir poussé votre clé publique dans /home/theophile/.ssh/authorized_keys, désactivez PasswordAuthentication pour durcir l’accès.

Valider le service Asterisk

Confirmez que les services SSH et Asterisk sont actifs et qu’Asterisk répond aux commandes de la CLI (Figure 20).

Bash
lxc exec asterisk01 -- systemctl is-active --quiet ssh && echo "SSH OK"
lxc exec asterisk01 -- systemctl is-active --quiet asterisk && echo "Asterisk OK"
lxc exec asterisk01 -- asterisk -rx "core show version"
Résultat attendu : les messages SSH OK et Asterisk OK doivent s’afficher. En cas d’erreur, consultez journalctl -u ssh ou journalctl -u asterisk depuis le conteneur.

Le conteneur asterisk01 est maintenant prêt pour la configuration SIP détaillée dans la Partie II.

1.4 Création d'un conteneur pour un client SIP

Ce second conteneur hébergera le poste client SIP ua01. Il reprend l'image Ubuntu 20.04, mais nous allons l'isoler du PBX avec son propre compte administrateur et un accès SSH maîtrisé afin de préparer l'installation de pjsua.

Bash
# Définir le nom du conteneur côté client
CT=${CT:-ua01}

# Créer le conteneur si besoin et attendre son IPv4 sur lxdbr0
if ! lxc info "${CT}" > /dev/null 2>&1; then
    lxc launch ubuntu:20.04 "${CT}"
fi

for attempt in $(seq 1 20); do
    IP=$(lxc list "${CT}" -c4 --format csv | cut -d' ' -f1)
    [ -n "$IP" ] && break
    sleep 1
done
echo "Client ${CT} -> ${IP:-en attente}"
Astuce : si la variable CT contenait déjà asterisk01, exécutez unset CT avant ce bloc pour éviter d'écraser le conteneur du PBX.

Préparer l'accès SSH

Installez le service SSH, appliquez une politique d'authentification explicite et neutralisez les réglages imposés par l'image cloud qui interdisent l'usage du mot de passe.

Bash
# Installation des outils de base côté client
lxc exec "${CT}" -- apt update
lxc exec "${CT}" -- apt install -y openssh-server sudo

# Drop-in pour autoriser clés et mots de passe
lxc exec "${CT}" -- bash -lc "cat <<'EOF' > /etc/ssh/sshd_config.d/99-allow-passwords.conf
PubkeyAuthentication yes
PasswordAuthentication yes
PermitEmptyPasswords no
EOF"

# Neutraliser la configuration cloud qui force PasswordAuthentication no
lxc exec "${CT}" -- sed -i 's/^PasswordAuthentication no/# PasswordAuthentication no/' /etc/ssh/sshd_config.d/60-cloudimg-settings.conf 2>/dev/null || true

# Activer le service et vérifier la configuration effective
lxc exec "${CT}" -- systemctl enable --now ssh
lxc exec "${CT}" -- sshd -T | egrep 'authentication'
Attention : le filtrage egrep doit retourner pubkeyauthentication yes et passwordauthentication yes. Si ce n'est pas le cas, inspectez l'ensemble des fichiers dans /etc/ssh/sshd_config.d.

Créer un utilisateur applicatif

Travaillez avec un compte non-root (tavenel par défaut) afin de pouvoir tester les enregistrements SIP comme un utilisateur classique.

Bash
NOM=${NOM:-tavenel}
lxc exec "${CT}" -- bash -lc "id -u ${NOM} || adduser --disabled-password --gecos '' ${NOM}"
lxc exec "${CT}" -- usermod -aG sudo "${NOM}"
lxc exec "${CT}" -- bash -lc "echo '${NOM}:${NOM}' | chpasswd"
Sécurité : imposez un mot de passe fort ou poussez immédiatement votre clé publique via lxc file push ~/.ssh/id_ed25519.pub ${CT}/home/${NOM}/.ssh/authorized_keys avant de désactiver l'authentification par mot de passe.

Valider la connexion depuis l'hôte

Récupérez l'adresse IPv4 du client puis ouvrez une session SSH pour contrôler que l'environnement est prêt pour la compilation de pjsua.

Bash
IP=$(lxc list "${CT}" -c4 --format csv | cut -d' ' -f1)
ssh -o StrictHostKeyChecking=accept-new "${NOM}@${IP}"
# À la première connexion, changez le mot de passe et vérifiez l'accès sudo.
Résultat attendu : vous devez obtenir un prompt ${NOM}@ua01:~$. Conservez l'adresse IPv4 : elle sera réutilisée pour enregistrer le client sur Asterisk.

1.5 Compilation de PJSIP et pjsua

Nous utilisons un script build_pjsua.sh exécuté dans le conteneur ua01 pour automatiser toute la chaîne de compilation de pjproject et du client pjsua. L'objectif est de pouvoir relancer le build à l'identique, quel que soit l'état précédent du conteneur.

Initialiser le script d'automatisation

Créez un squelette Bash strict : variables surchargables, fonctions de log et installation des dépendances.

Bash
# Depuis ua01, créer build_pjsua.sh
cat <<'EOF' > ~/build_pjsua.sh
#!/usr/bin/env bash
set -Eeuo pipefail
TAG=${TAG:-2.14.1}
PREFIX=${PREFIX:-/usr/local}
ENABLE_IPV6=${ENABLE_IPV6:-0}
FORCE_RECLONE=${FORCE_RECLONE:-0}
MAKE_JOBS=${MAKE_JOBS:-$(nproc)}
LOG_PREFIX="[pjsua]"

say()   { printf '%s %s\n'   "$LOG_PREFIX" "$*"; }
warn()  { printf '%s WARN %s\n' "$LOG_PREFIX" "$*" >&2; }
die()   { printf '%s ERROR %s\n' "$LOG_PREFIX" "$*" >&2; exit 1; }

install_deps() {
    say "Installation des dépendances de compilation"
    sudo apt update
    sudo apt install -y build-essential git pkg-config libssl-dev libasound2-dev \
        libncurses5-dev libreadline-dev libxml2-dev python3 python3-distutils
}
EOF
chmod +x ~/build_pjsua.sh
Paramètres utiles : ajustez TAG pour viser une version précise de pjproject, PREFIX pour définir la destination (par exemple /opt/pjsip) et ENABLE_IPV6=1 pour construire les bibliothèques avec support IPv6.

Étape 1 - Installer les dépendances et préparer les sources

Ajoutez une fonction qui clone ou met à jour le dépôt pjproject dans /usr/src, avec option de reclonage complet.

Bash
prepare_sources() {
    say "Préparation des sources pjproject"
    cd /usr/src
    if [[ "$FORCE_RECLONE" == "1" && -d pjproject ]]; then
        sudo rm -rf pjproject
    fi
    if [[ ! -d pjproject ]]; then
        sudo git clone https://github.com/pjsip/pjproject.git
    fi
    cd pjproject
    sudo git fetch --tags
    sudo git checkout -f "$TAG"
    sudo make distclean >/dev/null 2>&1 || true
}
Astuce : make distclean supprime les artefacts de builds précédents et évite les surprises lorsqu'on rejoue le script.

Étape 2 - Configurer et compiler pjproject

Prolongez le script avec la configuration du projet, la compilation et l'installation des bibliothèques partagées.

Bash
build_pjproject() {
    say "Configuration de pjproject"
    cd /usr/src/pjproject
    local extra_cflags="-fPIC"
    if [[ "$ENABLE_IPV6" == "1" ]]; then
        extra_cflags="${extra_cflags} -DPJ_HAS_IPV6=1"
    fi
    ./configure --prefix="$PREFIX" --enable-shared --disable-video \
        CFLAGS="$extra_cflags" || die "échec configure"

    say "Compilation et installation"
    make dep
    make ${MAKE_JOBS:+-j${MAKE_JOBS}}
    sudo make install
    sudo ldconfig
}
Build : la cible make dep est idempotente. Elle peut être relancée sans risque pour régénérer les dépendances.

Étape 3 - Compiler pjsua

Compilez ensuite l'application pjsua en respectant la structure attendue du Makefile.

Bash
build_pjsua() {
    say "Compilation de pjsua"
    cd /usr/src/pjproject
    [[ -f build.mak ]] || ./configure --prefix="$PREFIX"
    cd pjsip-apps/build
    make PJDIR=/usr/src/pjproject ${MAKE_JOBS:+-j${MAKE_JOBS}}

    say "Installation de pjsua"
    local bin
    bin=$(find ../bin -maxdepth 1 -type f -name 'pjsua-*' | head -n1)
    [[ -x "$bin" ]] || die "binaire pjsua introuvable"
    sudo install -Dm755 "$bin" "$PREFIX/bin/pjsua"
    sudo ldconfig
}
Contrôle : le chemin PJDIR doit pointer vers la racine des sources pour que le Makefile trouve ../../build.mak.

Étape 4 - Validation de l'installation

Terminez par une fonction de tests simples que vous invoquez depuis le script ou manuellement.

Bash
check_install() {
    say "Validation de pjsua"
    command -v pjsua || die "pjsua absent du PATH"
    pjsua --version
    pkg-config --modversion libpjproject || warn "pkg-config libpjproject indisponible"
    ldd "$PREFIX/bin/pjsua" | grep -E 'pjsua|pjsip|pjmedia|pjlib'
}

install_deps
prepare_sources
build_pjproject
build_pjsua
check_install
Résultat attendu : pjsua --version doit remonter la révision ${TAG}, les bibliothèques dynamiques doivent apparaître dans ldd et pkg-config doit connaître libpjproject.

Partie II : Premiers tests

Cette deuxième partie valide la pile ToIP après l'installation. Nous vérifions la configuration par défaut d'Asterisk, sécurisons un jeu de fichiers de secours, définissons un premier compte SIP interne, construisons un plan de numérotation minimaliste et enregistrons le client ua01 pour lancer nos appels de test.

2.1 Configuration minimale d'Asterisk

Avant d'écrire la moindre ligne de configuration, vérifiez que l'instance asterisk01 fonctionne correctement. L'objectif est double : s'assurer que l'installation de base expose bien tous les fichiers attendus et préparer une sauvegarde « point zéro » à réimporter en cas d'erreur.

  1. Lister et inspecter le contenu de /etc/asterisk (on doit retrouver les fichiers acl.conf, sip.conf, extensions.conf, etc. appartenant à l'utilisateur asterisk).
  2. Contrôler le service en vérifiant la version du PBX, l'état systemd et les modules clés chargés.
  3. Geler l'état d'origine dans /root/backup-ast avant d'écraser les fichiers.
Bash
# 1. Inventaire détaillé et version d'Asterisk
lxc exec asterisk01 -- bash -lc "ls -l /etc/asterisk"
lxc exec asterisk01 -- bash -lc "asterisk -rx 'core show version'"

# 2. Vérifier que le service tourne et que chan_sip est chargé
lxc exec asterisk01 -- bash -lc "systemctl is-active --quiet asterisk && echo 'Asterisk service: OK'"
lxc exec asterisk01 -- bash -lc "asterisk -rx 'module show like chan_sip'"

# 3. Optionnel : vérifier le port d'écoute par défaut (UDP 5060)
lxc exec asterisk01 -- bash -lc "asterisk -rx 'sip show settings' | egrep 'UDP Bind|Port:'"

# 4. Sauvegarde préventive des fichiers critiques
lxc exec asterisk01 -- bash -lc "\
  mkdir -p /root/backup-ast && \
  for f in sip.conf pjsip.conf extensions.conf; do \
    cp -a /etc/asterisk/$f /root/backup-ast/${f}.0 2>/dev/null || true; \
  done && ls -l /root/backup-ast"
À surveiller : les fichiers doivent appartenir à asterisk:asterisk en rw-r-----. La commande module show like chan_sip doit retourner chan_sip.so chargé, sinon installez le paquet asterisk correspondant avant d'aller plus loin.

Si l'une de ces vérifications échoue (service inactif, fichiers absents, module manquant), corrigez la situation immédiatement. Les étapes suivantes supposent que la pile SIP est opérationnelle et qu'un instantané propre existe dans /root/backup-ast.

2.2 Comptes SIP & 2.3 Plan de numérotation

Nous provisionnons à présent le compte ua01 côté serveur et remplaçons le dialplan par un scénario minimaliste centré sur l'extension 600. Comme dans le rapport (Figure 55), nous utilisons des blocs heredoc afin de conserver une configuration reproductible et versionnable.

Bash
# Ajouter le compte SIP ua01
lxc exec asterisk01 -- bash -lc "cat <<'EOF' > /etc/asterisk/sip.conf
[general]
context = default
allowoverlap = no
udpbindaddr = 0.0.0.0

[ua01]
type = friend
secret = ua01
host = dynamic
context = from-sip
dtmfmode = rfc2833
disallow = all
allow = ulaw
allow = alaw
EOF"

# Plan de numérotation minimal avec annonce de l'heure
lxc exec asterisk01 -- bash -lc "cat <<'EOF' > /etc/asterisk/extensions.conf
[general]
autofallthrough=yes

[from-sip]
exten => 600,1,Verbose(1,Appel interne vers extension 600)
exten => 600,n,Set(CHANNEL(language)=fr)
exten => 600,n,Answer()
exten => 600,n,Playback(demo-echotest)
exten => 600,n,Playback(vm-goodbye)
exten => 600,n,Hangup()
EOF"
Référence : Figure 55 illustre les deux blocs cat <<'EOF' utilisés pour injecter la section [ua01] et le contexte [from-sip]. Conservez cette approche pour pouvoir rejouer la configuration à l'identique.

Rechargez ensuite les modules SIP et le dialplan comme dans la Figure 56, puis contrôlez l'état du pair. Attendez-vous à voir ua01 dans la liste mais en statut « Unspecified » tant qu'aucun agent ne s'est enregistré.

Bash
# Recharger chan_sip et le dialplan
lxc exec asterisk01 -- bash -lc "sudo -u asterisk asterisk -rx 'sip reload'"
lxc exec asterisk01 -- bash -lc "sudo -u asterisk asterisk -rx 'dialplan reload'"

# Vérifier la présence du pair ua01
lxc exec asterisk01 -- bash -lc "asterisk -rx 'sip show peers' | grep -n ua01 --color=never"

Nous interrogeons ensuite Asterisk avec asterisk -rx 'sip show settings' | egrep 'UDP Bind|TCP Bind|Port:' afin de vérifier l'adresse et le port d'écoute de chan_sip. Le résultat attendu est un binding UDP sur 0.0.0.0:5060, preuve que le PBX acceptera la signalisation depuis l'extérieur. Si ufw protège le conteneur, autorisez explicitement 5060/udp pour la signalisation et la plage 10000:20000/udp pour les flux RTP : vous devez obtenir un retour « Rule added » puis « Rules updated » confirmant l'application des règles.

Sortie attendue
Name/username             Host            Dyn Forcerport Comedia    ACL Port     Status
ua01/ua01                 (Unspecified)    D  Auto (No)  No             0        Unmonitored
Interprétation : le champ Host reste « Unspecified » avant l'enregistrement du client. Le drapeau D confirme que l'adresse sera apprise dynamiquement, ce qui est l'effet recherché.

Pour préparer les tests d'appel, sécurisez enfin l'écoute SIP et la plage RTP. La Figure 57 du rapport montre la commande sip show settings qui doit indiquer un bind UDP sur 0.0.0.0:5060. Si ufw est actif dans le conteneur, ouvrez explicitement le port SIP et la plage média.

Bash
# Vérifier l'écoute et ajuster le pare-feu si besoin
lxc exec asterisk01 -- bash -lc "asterisk -rx 'sip show settings' | egrep 'UDP Bind|Port:'"
lxc exec asterisk01 -- bash -lc "ufw allow 5060/udp"  # si UFW est utilisé
lxc exec asterisk01 -- bash -lc "ufw allow 10000:20000/udp"
Sortie attendue
UDP Bindaddress                      0.0.0.0:5060
UDP Bindport                         0
...
Rule added
Rules updated

À ce stade, le PBX est prêt à accepter l'enregistrement du client ua01 et à desservir l'extension 600. Conservez les fichiers sauvegardés dans /root/backup-ast : ils serviront de point de restauration si vous expérimentez d'autres scénarios de numérotation.

2.4 Association d'un User Agent SIP

L'objectif est maintenant d'enregistrer le client ua01 sur le PBX et de vérifier qu'il peut établir un appel vers l'extension 600. Nous reprenons les étapes du rapport (Figures 58 à 66) : récupération de l'adresse du serveur, enregistrement interactif avec pjsua, observation du défi 401, validation du 200 OK, appel de test et contrôles côté Asterisk.

  1. Récupérer l'adresse du PBX et lancer pjsua avec des identifiants explicites.
  2. Observer le 401 / 200 OK dans la console pjsua pour confirmer l'authentification Digest.
  3. Automatiser un appel sortant vers 600 et vérifier les statistiques RTP.
  4. Contrôler l'inscription côté PBX ainsi que l'état du service Asterisk.
Bash
# 1. Lancer pjsua en mode interactif (Figure 58)
ASTIP=$(lxc list asterisk01 -c4 --format=csv | cut -d' ' -f1)
export ASTIP
lxc exec ua01 -- bash -lc "pjsua --null-audio \
  --id sip:ua01@$ASTIP --registrar sip:$ASTIP \
  --realm '*' --username ua01 --password ua01"

Les premières lignes affichent le chargement des modules pjsua, puis un REGISTER envoyé au PBX. Ce dernier répond d'abord 401 Unauthorized (défi Digest). pjsua renvoie automatiquement un second REGISTER avec l'en-tête Authorization calculé à partir du couple ua01/ua01, ce qui doit se solder par SIP/2.0 200 OK (Figure 61).

Sortie attendue
TX 400 REGISTER sip:<ASTIP>;transport=UDP
RX 401 Unauthorized (nonce=...)
TX 401 REGISTER (cseq 29339) with Authorization
RX 200 OK (Contact: sip:ua01@<IP-UA01>;expires=300)

Dans un autre terminal, contrôlez immédiatement la présence du pair ua01 côté PBX :

Bash
lxc exec asterisk01 -- bash -lc "asterisk -rx 'sip show peers' | grep -n ua01 --color=never"
Sortie attendue
2:ua01/ua01                 10.142.96.187   D  Auto (No)  No  5060  Unmonitored

Le champ Host doit maintenant afficher l'adresse IP réelle du conteneur ua01, ce qui confirme l'enregistrement dynamique.

Appel automatisé vers l'extension 600

Reprenez la Figure 65 pour automatiser l'appel : on enregistre ua01, on lance un INVITE vers 600, puis la session se termine automatiquement après cinq secondes.

Bash
# 2. Appel non interactif et statistiques RTP (Figure 62)
lxc exec ua01 -- bash -lc "pjsua --null-audio --duration=5 \
  --id sip:ua01@$ASTIP --registrar sip:$ASTIP --realm '*' \
  --username ua01 --password ua01 sip:600@$ASTIP"
Sortie attendue
>> Call: 0 is CONNECTED [media established]
...
>>> Call: 0 duration=00h00m05s, srv-calls=1, srv-failed=0
   RX pt=0, stat last update: 5.0 s ago
   RX total: 400 packets, 32000 bytes (0 lost)
   TX total: 400 packets, 32000 bytes (0 lost)
>>> Call 0: disconnected (Reason=200/Normal call clearing)

Après la libération de l'appel, vérifiez que le pair reste enregistré et que le service Asterisk est actif :

Bash
lxc exec asterisk01 -- bash -lc "asterisk -rx 'sip show peers' | grep -n ua01 --color=never"
lxc exec asterisk01 -- bash -lc "systemctl is-active --quiet asterisk && echo 'Asterisk service: OK'"

Vous pouvez également vérifier que pjsua n'est plus actif côté client et nettoyer les sessions résiduelles le cas échéant (Figure 64) :

Bash
lxc exec ua01 -- ps -eo pid,cmd | grep pjsua | grep -v grep || echo "pjsua arrêté"
Résultat attendu : le pair ua01 reste enregistré avec son adresse IP, l'appel vers 600 se termine proprement avec un BYE de la part d'Asterisk et aucune alerte critique n'apparaît dans les journaux (asterisk -rx 'core show channels' doit retourner zéro canal actif).

Conservez enfin une copie de la configuration validée (/root/backup-ast) avant d'ajouter de nouveaux comptes ou extensions. Vous disposez maintenant d'une chaîne complète de test pour rejouer rapidement les enregistrements et appels de validation.