Table of Contents

Mise en place d'un réflecteur public SVXLink

Au moment où ce guide a été fait, le code de svxlink était en version 17.12.1. Si la version a changé depuis, vous pouvez tenter d'appliquer les patches, tout en faisant attention à corriger sur les noms des dossiers.

Patchage et construction du paquet

La dernière version fournie de Svxreflector ne permet pas l'utilisation d'un mot de passe commun pour tous les clients qui veulent se connecter. D'autre part, la connexion de relais avec l'utilisateur par défaut dans la configuration (5P07N1K) n'est pas souhaitable. Pour pallier à ces problèmes, il faut reconstruire le paquet svxreflector après avoir appliqué des patchs.

Pour ce faire, en tant que root, se mettre dans le dossier /usr/local/src/, télécharger les dépendances nécessaires à la construction de svxlink, les sources du paquet et les outils nécessaires à la construction du paquet:

apt-get update
apt-get install quilt debhelper build-essential
apt-get build-dep svxreflector
apt-get source svxreflector

Ajouter les fichiers suivants dans le dossier /usr/local/src/:

f4hof-add-svxreflector-default-pwd-group.patch

Index: svxlink-17.12.1/src/svxlink/reflector/ReflectorClient.cpp
===================================================================
--- svxlink-17.12.1.orig/src/svxlink/reflector/ReflectorClient.cpp
+++ svxlink-17.12.1/src/svxlink/reflector/ReflectorClient.cpp
@@ -520,9 +520,8 @@ std::string ReflectorClient::lookupUserK
   string auth_group;
   if (!m_cfg->getValue("USERS", callsign, auth_group) || auth_group.empty())
   {
-    cout << "*** WARNING: Unknown user \"" << callsign << "\""
-         << endl;
-    return "";
+    /* Unknown user, silently hinting to group DefaultPassword. */
+    auth_group = "DefaultPassword";
   }
   string auth_key;
   if (!m_cfg->getValue("PASSWORDS", auth_group, auth_key) || auth_key.empty())

f4hof-reject-anon.patch

Index: svxlink-17.12.1/src/svxlink/reflector/ReflectorClient.cpp
===================================================================
--- svxlink-17.12.1.orig/src/svxlink/reflector/ReflectorClient.cpp
+++ svxlink-17.12.1/src/svxlink/reflector/ReflectorClient.cpp
@@ -518,6 +518,12 @@ void ReflectorClient::handleHeartbeat(As
 std::string ReflectorClient::lookupUserKey(const std::string& callsign)
 {
   string auth_group;
+  if (strcasestr( callsign.c_str(), "5P07N1K") != NULL)
+  {
+    /* Deny connection to anons */
+    cout << "Attempt to connect as \"" << callsign << "\" denied." << endl;
+    return "";
+  }
   if (!m_cfg->getValue("USERS", callsign, auth_group) || auth_group.empty())
   {
     /* Unknown user, silently hinting to group DefaultPassword. */

Une fois cette étape réalisée, on passe à l'application des patch et à la compilation des paquets:

cd svxlink-17.12.1
quilt import ../f4hof-add-svxreflector-default-pwd-group.patch
quilt import ../f4hof-reject-anon.patch
quilt push f4hof-add-svxreflector-default-pwd-group.patch
quilt push f4hof-reject-anon.patch
dpkg-buildpackage -us -uc -b

Votre paquet, prêt à être installé, est dans le dossier /usr/local/src.

Installation du paquet

Installer, puis figer le paquet, pour éviter que la version du dépôt vienne écraser votre version, lorsque faites une mise à jour. Attention, le nom du fichier du paquet va dépendre de l'architecture matérielle de votre machine.

Votre paquet devrait avoir un nom structuré de la manière suivante: svxreflector_<version>_<archi>.deb

cd /usr/local/src
dpkg -i svxreflector_17.12.1-2_amd64.deb
apt-mark hold svxreflector

Paramétrage environnement

Si vous n'avez pas d'utilisateur et de groupe svxlink, il faut le créer, ainsi qu'un répertoire dédié pour les logs. Le script suivant permet de le faire pour vous.

#!/bin/bash
grep svxlink /etc/passwd &> /dev/null
if [[ "$?" != "0" ]]; then
  adduser --system --group --disabled-password svxlink
  useradd -rG audio,plugdev,gpio svxlink
fi

if [ ! -d /var/log/svxlink ]; then
  mkdir /var/log/svxlink
  chown svxlink:adm /var/log/svxlink
fi

Fichier de configuration et unité systemd

Pour héberger une seule instance

Si vous ne souhaitez mettre en place qu'un seul réflecteur (aussi appelé salon par certains outils francophones), la configuration suivante vous permettra de disposer d'un réflecteur sur le port 5300. Remplacer “VotreMotDePasseDeGroupe” par un mot de passe de votre choix pour le salon.

Si votre serveur est situé derrière une box, ou un NAT, n'oubliez pas d'ajouter une règle de redirection de port, et une règle firewall pour autoriser les connexions entrantes.

Créer le fichier /etc/svxlink/svxreflector.conf

###################################################################
#
# Configuration file for the SvxReflector SvxLink conference node
#
###################################################################

[GLOBAL]
#CFG_DIR=svxreflector.d
TIMESTAMP_FORMAT="%Y-%m-%dT%H:%M:%S%z"
LISTEN_PORT=5300
#SQL_TIMEOUT=600
#SQL_TIMEOUT_BLOCKTIME=60
CODECS=OPUS

[USERS]

[PASSWORDS]
DefaultPassword="VotreMotDePasseDeGroupe"

Créer le fichier /etc/systemd/system/svxreflector.service

[Unit]
Description=SvxLink Reflector
Documentation=man:svxreflector(1)
After=network.target remote-fs.target time.target

[Service]
Type=simple
User=svxlink
Group=daemon
EnvironmentFile=-/etc/default/svxreflector
Environment=CFGFILE=/etc/svxlink/svxreflector.conf
Environment=LOGFILE=/var/log/svxlink/svxreflector.log
RuntimeDirectory=svxlink
ExecStart=/usr/bin/svxreflector --config $CFGFILE --logfile $LOGFILE
ExecReload=/bin/kill -s HUP $MAINPID
Restart=on-failure
TimeoutStartSec=60
[Install]
WantedBy=multi-user.target

Pour héberger plusieurs instances

Reproduire la procédure ci-dessus en modifiant le nom du fichier de conf par un nom comprenant le nom de votre réflecteur.

Par exemple, si vous voulez faire 3 réflecteurs: un général, un technique et un urgence, faites 3 fichiers de configuration nommés /etc/svxlink/svxreflector-general.conf, /etc/svxlink/svxreflector-tech.conf, et /etc/svxlink/svxreflector-urgence.conf, où vous prendrez soin à choisir un numéro de port unique (par exemple, 5300, 5301 et 5302). L'utilisation d'un même mot de passe entre 2 salons n'est pas gênante.

Pour les unités systemd, même principe: créer des noms d'unités qui comprennent le nom de votre réflecteur, collez le template ci-dessus et modifiez les lignes suivantes:

Description=SvxLink Reflector serving <nom-reflecteur> room
EnvironmentFile=-/etc/default/svxreflector-<nom-reflecteur>
Environment=CFGFILE=/etc/svxlink/svxreflector-<nom-reflecteur>.conf
Environment=LOGFILE=/var/log/svxlink/svxreflector-<nom-reflecteur>.log

Par exemple, pour le réflecteur tech, créer le fichier /etc/systemd/system/svxreflector-tech.service qui contient:

[Unit]
Description=SvxLink Reflector serving tech room
Documentation=man:svxreflector(1)
After=network.target remote-fs.target time.target

[Service]
Type=simple
User=svxlink
Group=daemon
EnvironmentFile=-/etc/default/svxreflector-tech
Environment=CFGFILE=/etc/svxlink/svxreflector-tech.conf
Environment=LOGFILE=/var/log/svxlink/svxreflector-tech.log
RuntimeDirectory=svxlink
ExecStart=/usr/bin/svxreflector --config $CFGFILE --logfile $LOGFILE
ExecReload=/bin/kill -s HUP $MAINPID
Restart=on-failure
TimeoutStartSec=60
[Install]
WantedBy=multi-user.target

Activation des services

Pour que les changements soient pris en compte, puis activer au démarrage et démarrer votre unité:

systemctl daemon-reload
systemctl enable svxreflector.service
systemctl start svxreflector.service

Pour contrôler si le service tourne:

systemctl status svxreflector.service

Pour consulter le journal systemd du service:

journalctl -u svxreflector.service

Paramétrage relais

Pour commencer, vous devez choisir un nom abrégé pour votre réflecteur, à trois lettres.

Fichier audio de nom de salon

Ce fichier permet d'annoncer le nom de votre réflecteur lorsqu'un relai va basculer dessus.

Le format de fichier validé pour les annonces de nom de conférence est le suivant: WAVE PCM 16 bits signé, échantillonnage à 16kHz, mono.

Si vous voulez convertir un fichier dans ce format à l'aide de ffmpeg, voici la commande:

ffmpeg -i <fichier_source> -c 1 -ar 16000 -acodec pcm_s16le Sxmp.wav

Le nom du fichier doit respecter la forme S<nom_abrege_reflecteur>.wav (par ex: Sxmp.wav ).

Fichier restart

Vous devez également mettre à disposition un fichier restart.

Le nom du fichier est de la forme restart.<nom_abrege_reflecteur>.

#!/bin/bash

# DTMF 123  xyz #

# Stop svxlink
pkill -TERM svxlink
sleep 3

#/etc/spotnik/audio.sh
#sleep 1

# Save network
echo "xyz" > /etc/spotnik/network

# Replace announcement
rm /usr/share/svxlink/sounds/fr_FR/PropagationMonitor/name.wav
ln -s /etc/spotnik/Sxyz.wav /usr/share/svxlink/sounds/fr_FR/PropagationMonitor/name.wav

# Update config
rm -f /etc/spotnik/svxlink.xyz
cat /etc/spotnik/svxlink.cfg >/etc/spotnik/svxlink.xyz
echo "HOST=svxlink.example.org" >> /etc/spotnik/svxlink.xyz
echo "AUTH_KEY=VotreMotDePasseDeGroupe" >> /etc/spotnik/svxlink.xyz
echo "PORT=5300" >> /etc/spotnik/svxlink.xyz

# Clear logs
> /tmp/svxlink.log

# Launch svxlink
svxlink --daemon --logfile=/tmp/svxlink.log --pidfile=/var/run/svxlink.pid --runasuser=root --config=/etc/spotnik/svxlink.xyz

# Enable propagation monitor module
#sleep 1
#echo "10#" > /tmp/svxlink_dtmf_ctrl_pty

Remplacez dans ce fichier:

sed -i 's/xyz/abc/g' restart.abc

Procédure de paramétrage relais

Fournissez le fichier audio et le fichier restart.

Ci-dessous, un modèle de procédure que je communique d'habitude. (pour l'exemple, notre réflecteur s'appelle XYZ, et on y accèdera en tapant 123#)

Bonjour,

Voici la procédure pour ajouter l'accès au réflecteur XYZ sur un relais spotnik 2.0:

Copier les 2 fichiers ci-joint dans le dossier /etc/spotnik de ton relais.
Rendre le fichier /etc/spotnik/restart.xyz exécutable ( chmod a+x /etc/spotnik/restart.xyz )
Ensuite, éditer le fichier /usr/share/svxlink/events.d/local/Logic.tcl.
Chercher la ligne "# 96 SvxReflector RRF", et coller le bloc suivant avant celle-ci:


# 123 SvxReflector XYZ

  if {$cmd == "123"} {
    puts "Executing external command"
    playMsg "Core" "online"
    exec nohup /etc/spotnik/restart.xyz &
    return 1
  }

Une fois l'édition terminée, la commande ne sera pas prise en compte immédiatement.
Il faut soit basculer sur un module existant ( 95# par exemple ), soit passer le relais à la main sur le réflecteur XYZ au moins une fois ( taper la commande /etc/spotnik/restart.xyz sur le terminal ).

Je reste à dispo en cas de questions.

Cordialement,

Plan de commande relais

Mosquitto

Remplacer admin-user par le nom d'utilisateur de votre administrateur pour le plugin de sécurité dynamique.

apt install mosquitto
mosquitto_ctrl dynsec init /var/lib/mosquitto/dynamic-security.json admin-user
chown mosquitto:mosquitto /var/lib/mosquitto/dynamic-security.json
/etc/mosquitto/conf.d/general.conf
listener 1883
allow_anonymous false
per_listener_settings false

plugin /usr/lib/x86_64-linux-gnu/mosquitto_dynamic_security.so
plugin_opt_config_file /var/lib/mosquitto/dynamic-security.json

FIXME TODO

systemctl restart mosquitto.service
ufw allow from any to any port 1883
 
mosquitto_ctrl dynsec createRole repeater
mosquitto_ctrl dynsec addRoleACL repeater subscribePattern repeaters/%u allow 0
mosquitto_ctrl dynsec addRoleACL repeater publishClientSend repeaters/%u/ack allow 0
 
mosquitto_ctrl dynsec createRole masterController
mosquitto_ctrl dynsec addRoleACL masterController publishClientSend repeaters/+ allow 0
mosquitto_ctrl dynsec addRoleACL masterController subscribePattern repeaters/+/ack allow 0
 
mosquitto_ctrl dynsec createGroup gr_admin
mosquitto_ctrl dynsec createGroup gr_master_controllers
mosquitto_ctrl dynsec createGroup gr_repeaters
mosquitto_ctrl dynsec addGroupRole gr_admin admin
mosquitto_ctrl dynsec addGroupRole gr_repeaters repeater
mosquitto_ctrl dynsec addGroupRole gr_master_controllers masterController

Where acltype is one of publishClientSend, publishClientReceive, subscribeLiteral, subscribePattern, unsubscribeLiteral, and unsubscribePattern.

https://mosquitto.org/documentation/dynamic-security/#creating-and-modifying-roles

Diffusion d'un salon sur Icecast

Cette section décrit la procédure qui permet d'ajouter la rediffusion d'un salon avec Icecast.

Architecture Générale

   ______________                _________                        _________                _________  
  |   Instance   |              | Client  |                      |         |              |         | 
  | SVXReflector |===[socket]===| SVXLink |===[Loopback audio]===| DarkICE |===[socket]===| Icecast | 
  |______________|              |_________|                      |_________|              |_________| 

Installation des prérequis

Lancer les commandes suivantes en tant que root:

apt install darkice icecast2

Configuration d'Icecast

Le fichier de configuration suivant permet de servir les flux de diffusion avec une socket non sécurisée, et une socket sécurisée (utile pour mettre un lecteur sur une page web en HTTPS).

Les valeurs des champs suivants DOIT être modifiée:

<icecast>
    <location>Ma localisation</location>
    <admin>icemaster@example.com</admin>
    <limits>
        <clients>100</clients>
        <sources>10</sources>
        <queue-size>524288</queue-size>
        <client-timeout>30</client-timeout>
        <header-timeout>15</header-timeout>
        <source-timeout>10</source-timeout>
        <burst-on-connect>0</burst-on-connect>
        <burst-size>65535</burst-size>
    </limits>
 
    <authentication>
        <source-password>mdp_injection_changezmoi</source-password>
        <relay-password>mdp_relai_changezmoi</relay-password>
 
        <admin-user>admin</admin-user>
        <admin-password>mdp_admin_changezmoi</admin-password>
    </authentication>
    <hostname>icecast.example.com</hostname>
 
    <!-- You may have multiple <listener> elements -->
    <listen-socket>
        <port>5299</port>
    </listen-socket>
 
    <listen-socket>
        <port>8080</port>
        <bind-address>127.0.0.1</bind-address>
    </listen-socket>
 
 
    <listen-socket>
        <port>5243</port>
        <ssl>1</ssl>
    </listen-socket>
 
    <http-headers>
        <header name="Access-Control-Allow-Origin" value="*" />
    </http-headers>
    <fileserve>1</fileserve>
 
    <paths>
        <basedir>/usr/share/icecast2</basedir>
        <logdir>/var/log/icecast2</logdir>
        <webroot>/usr/share/icecast2/web</webroot>
        <adminroot>/usr/share/icecast2/admin</adminroot>
        <alias source="/" destination="/status.xsl"/>
        <ssl-certificate>/etc/icecast2/bundle.pem</ssl-certificate>
    </paths>
 
    <logging>
        <accesslog>access.log</accesslog>
        <errorlog>error.log</errorlog>
        <loglevel>3</loglevel> <!-- 4 Debug, 3 Info, 2 Warn, 1 Error -->
        <logsize>10000</logsize> <!-- Max size of a logfile -->
    </logging>
 
    <security>
        <chroot>0</chroot>
    </security>
</icecast>

Si vous n'utilisez pas de connexion TLS, commentez ou supprimez le bloc listen-socket qui contient une balise ssl, et supprimez la ligne avec la balise ssl-certificate.

La génération des certificats est laissée à votre discrétion, à chaque renouvellement, vous devez regénérer le fichier /etc/icecast2/bundle.pem en concaténant, au format PEM, dans l'ordre suivant:

  1. La chaîne de certification dans l'ordre descendant.
  2. Le certificat utilisé par le serveur.
  3. La clé privée, non protégée par un mot de passe.

Les permissions de votre fichier bundle.pem doivent être positionnées à 600.

Ajout des interfaces de bouclage

Lancer les commandes suivantes pour créer une interface de bouclage.

echo "options snd-aloop enable=1 index=10" > /etc/modprobe.d/aloop.conf
echo "snd-aloop" >> /etc/modules
modprobe snd-aloop

Lancer les commandes suivantes pour créer quatre interfaces de bouclage.

echo "options snd-aloop enable=1,1,1,1 index=10,11,12,13" > /etc/modprobe.d/aloop.conf
echo "snd-aloop" >> /etc/modules
modprobe snd-aloop

Le code précédent permet de: - Définir des options à passer implicitement au chargement du module snd-aloop. - Ajouter snd-aloop dans la liste des modules à charger automatiquement à chaque boot. - Charger le module manuellement pour cette fois-ci sans avoir à rebooter.

Si vous voulez modifier le nombre d'interfaces, il faudra modifier /etc/modprobe.d/aloop.conf pour ajouter/supprimer des interfaces. Les index de chaque interface sont séparés par une virgule, et pensez bien à mettre à jour le nombre de 1 correspondant dans le champ enable pour que vos interfaces soient actives par défaut.

Pour appliquer les changements sans rebooter votre serveur, il faut arrêter tous les programmes qui utilise les interfaces de bouclage, puis lancer la commande suivante en tant que root, pour que les nouveaux paramètres soient pris en compte:

modprobe -r snd-aloop; modprobe snd-aloop

Un client SVXLink doit être configuré pour chaque réflecteur.

Reprendre le patron de configuration suivant, et changer le contenu de ces champs:

###############################################################################
#                                                                             #
#                Configuration file for the SvxLink client                    #
#                                                                             #
###############################################################################
 
[GLOBAL]
LOGICS=SimplexLogic,ReflectorLogic
CFG_DIR=svxlink.d
TIMESTAMP_FORMAT="%c"
CARD_SAMPLE_RATE=48000
LINKS=ALLlink
 
[SimplexLogic]
TYPE=Simplex
RX=NONE
TX=TxStream
CALLSIGN=ICECAST
EVENT_HANDLER=/usr/share/svxlink/events.tcl
DEFAULT_LANG=en_US
 
[ReflectorLogic]
TYPE=Reflector
HOST=127.0.0.1
PORT=5310
CALLSIGN="ICECAST"
AUTH_KEY="MotDePasseSvxReflector"
AUDIO_CODEC=OPUS
EVENT_HANDLER=/usr/share/svxlink/events.tcl
 
[ALLlink]
CONNECT_LOGICS=SimplexLogic:94:SK3AB,ReflectorLogic
DEFAULT_ACTIVE=1
TIMEOUT=0
 
[TxStream]
TYPE=Local
AUDIO_DEV=alsa:hw:10,0
AUDIO_CHANNEL=0
PTT_TYPE=NONE
TIMEOUT=7200
TX_DELAY=0
PREEMPHASIS=0

Sauver le résultat modifié dans /etc/svxlink/svxlink-nomreflecteur.conf.

Configuration de Darkice

Editer votre fichier /etc/hosts pour faire boucler le FQDN de votre serveur icecast sur votre machine (pour éviter les boucles sur le réseau local, notamment si vous êtes derrière un NAT en IPv4).

127.0.0.1 localhost icecast.example.com

Dans le dossier /etc/darkice, créer un fichier de configuration par réflecteur.

my-reflector.cfg
[general]
duration        = 0        # duration of encoding, in seconds. 0 means forever
bufferSecs      = 1         # size of internal slip buffer, in seconds
reconnect       = yes       # reconnect to the server(s) if disconnected
realtime        = no
rtpio           = 3
 
 
[input]
device          = hw:10,1
sampleRate      = 48000     # sample rate in Hz. try 11025, 22050 or 44100
bitsPerSample   = 16        # bits per sample. try 16
channel         = 2         # channels. 1 = mono, 2 = stereo
 
 
[icecast2-0]
bitrateMode     = cbr
format          = opus
bitrate         = 64
#quality         = 0.8
channel         = 1
lowpass         = -1       #Débrayage des filtres
highpass        = -1       #Débrayage des filtres
server          = 127.0.0.1  # host name of the server
port            = 5299       # port of the IceCast2 server, usually 8000
password        = mdp_injection_changezmoi    # source password to the IceCast2 server
mountPoint      = my-reflector  # mount point of this stream on the server
name            = Reflecteur-ARA # name of the stream
description     = Vous ecoutez le reflecteur Auvergne-Rhone-Alpes # description of the stream
url             = http://icecast.example.com/my-reflector  # URL related to the stream
genre           = Ham Radio  ,  Radioamateur    # genre of the stream
public          = yes       # advertise this stream on Icecast's status page?

Fichier d'unité à déposer sur /etc/systemd/system/darkice@.service:

darkice@.service
[Unit]
Description=Darkice server for room %I
Documentation=man:darkice(1)
After=network.target time.target sound.target icecast2.service
 
[Service]
Type=simple
User=svxlink
Group=svxlink
EnvironmentFile=-/etc/default/darkice-%i
Environment=CFGFILE=/etc/darkice/%i.cfg
#RuntimeDirectory=/etc/darkice
ExecStart=/usr/bin/darkice -c $CFGFILE
ExecStop=/bin/kill -s TERM $MAINPID
Restart=on-failure
TimeoutStartSec=60
 
[Install]
WantedBy=multi-user.target

Lancer la commande suivante en tant que root pour rafraîchir la config de systemd

systemctl daemon-reload

Ensuite, activer votre instance nommée de darkice pour qu'elle se lance à chaque boot, et lancez-la manuellement:

systemctl enable darkice@my-reflector
systemctl start darkice@my-reflector

Répéter cette opération pour l'ensemble des salons à diffuser.