Table of Contents

SVXReflector VM

Users:

root / toor (SSH access disabled)
sysop / hamradio

Config editor

Requirements for a room:

Nice to have, not scheduled, yet.

VM Build instructions

Install a debian buster/bullseye with SSH server and essential system utilities

# SSH Security improvement
sed -i 's/#PermitRootLogin .\+/PermitRootLogin without-password/' /etc/ssh/sshd_config
systemctl reload sshd.service

# Install packages
apt install darkice icecast2 lighttpd php7.4-cgi ufw 

# Setup Lighttpd
lighty-enable-mod fastcgi
lighty-enable-mod fastcgi-php
echo "include \"/var/svx/www-*.conf\"" >> /etc/lighttpd/lighttpd.conf
systemctl reload lighttpd.service

# Install Svxreflector
cd ~
SYSVER=$(grep VERSION_ID /etc/os-release | sed 's/"/ /g' | awk '{print $2;}')
if [[ "$SYSVER" == "10" ]]
then
  wget "http://f4hof.net/lib/exe/fetch.php/brouillons:svxreflector_17.12.1-2.1_amd64.deb"
elif [[ "$SYSVER" == "11" ]]
then
  wget "http://f4hof.net/lib/exe/fetch.php/brouillons:svxreflector_19.09.1-3.1_amd64.deb"
else
  echo "Unsupported system version"
  exit 1
fi
dpkg -i ~/svxreflector*.deb
apt install -f
apt-mark hold svxreflector
apt install svxlink-server

# Creating the application environment
mkdir -p /var/svx/
chown www-data:www-data /var/svx/
systemctl restart lighttpd.service

# Setup web iface


# Firewalling
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 5299/tcp
yes | ufw enable

Patched Svxreflector

Precompiled NMU packages

For Debian Buster

For Debian Bullseye

Howto build packages

# Build patched svxreflector
cd /usr/local/src/
apt install build-essential
apt build-dep svxreflector
apt source svxreflector
cd /usr/local/src/svxlink-*/debian/patches
# TODO: copy f4hof-*.patch in current directory
echo f4hof-add-svxreflector-default-pwd-group.patch >> series
echo f4hof-reject-anon.patch >> series
cd /usr/local/src/svxlink-*/
quilt push -a
dch --nmu
# Edit the Changelog
dpkg-buildpackage -us -uc
# Packages will be available in /usr/local/src/

Version 17.12.1

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. */

Version 19.09.1

f4hof-add-svxreflector-default-pwd-group.patch
Index: svxlink-19.09.1/src/svxlink/reflector/ReflectorClient.cpp
===================================================================
--- svxlink-19.09.1.orig/src/svxlink/reflector/ReflectorClient.cpp
+++ svxlink-19.09.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-19.09.1/src/svxlink/reflector/ReflectorClient.cpp
===================================================================
--- svxlink-19.09.1.orig/src/svxlink/reflector/ReflectorClient.cpp
+++ svxlink-19.09.1/src/svxlink/reflector/ReflectorClient.cpp
@@ -524,6 +524,12 @@ std::string ReflectorClient::lookupUserK
     auth_group = "DefaultPassword";
   }
   string auth_key;
+  if (strcasestr( callsign.c_str(), "5P07N1K") != NULL)
+  {
+    /* Deny connection to anons */
+    cout << "Attempt to connect as \"" << callsign << "\" denied." << endl;
+    return "";
+  }
   if (!m_cfg->getValue("PASSWORDS", auth_group, auth_key) || auth_key.empty())
   {
     cout << "*** ERROR: User \"" << callsign << "\" found in SvxReflector "

Systemd Units

svxreflector@.service
[Unit]
Description=SvxLink Reflector for room %I
Documentation=man:svxreflector(1)
After=network.target remote-fs.target time.target
 
[Service]
Type=simple
User=svxlink
Group=daemon
EnvironmentFile=-/etc/default/svxreflector-%i
Environment=CFGFILE=/var/svx/svxreflector-%i.conf
Environment=LOGFILE=/var/log/svxlink/svx-%i.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
svxlink@.service
[Unit]
Description=SvxLink client for room %I
Documentation=man:svxlink(1)
After=network.target remote-fs.target time.target sound.target svxreflector@%i.service
 
[Service]
Type=simple
User=svxlink
Group=daemon
EnvironmentFile=-/etc/default/svxlink-%i
Environment=CFGFILE=/var/svx/svxlink-%i.conf
Environment=LOGFILE=/var/log/svxlink/svxlink-%i.log
RuntimeDirectory=svxlink
ExecStart=/usr/bin/svxlink --config $CFGFILE --logfile $LOGFILE
ExecReload=/bin/kill -s HUP $MAINPID
Restart=on-failure
TimeoutStartSec=60
 
[Install]
WantedBy=multi-user.target
darkice@.service
[Unit]
Description=Darkice server for room %I
Documentation=man:darkice(1)
After=network.target time.target icecast2.service sound.target
 
[Service]
Type=simple
User=svxlink
Group=svxlink
EnvironmentFile=-/etc/default/darkice-%i
Environment=CFGFILE=/var/svx/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

Templates

icecast.xml
<icecast>
    <location>CHANGEME_LOC</location>
    <admin>CHANGEME_ADMIN_EMAIL</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>1</burst-on-connect>
        <burst-size>65535</burst-size>
    </limits>
 
    <authentication>
        <!-- Sources log in with username 'source' -->
        <source-password>CHANGEME_SRC_PWD</source-password>
        <!-- Relays log in with username 'relay' -->
        <relay-password>CHANGEME_RLY_PWD</relay-password>
 
        <!-- Admin logs in with the username given below -->
        <admin-user>sysop</admin-user>
        <admin-password>CHANGEME_ADMIN_PWD</admin-password>
    </authentication>
 
    <hostname>CHANGEME_HOSTNAME</hostname>
 
    <!-- You may have multiple <listener> elements -->
    <listen-socket>
        <port>5299</port>
    </listen-socket>
 
    <listen-socket>
        <port>8080</port>
    </listen-socket>
 
    <http-headers>
        <header name="Access-Control-Allow-Origin" value="*" />
    </http-headers>
 
    <!-- Normal mounts -->
 
    <fileserve>1</fileserve>
 
    <paths>
        <!-- basedir is only used if chroot is enabled -->
        <basedir>/usr/share/icecast2</basedir>
        <logdir>/var/log/icecast2</logdir>
        <webroot>/usr/share/icecast2/web</webroot>
        <adminroot>/usr/share/icecast2/admin</adminroot>
        <!-- <pidfile>/usr/share/icecast2/icecast.pid</pidfile> -->
        <alias source="/" destination="/status.xsl"/>
    </paths>
 
    <logging>
        <accesslog>access.log</accesslog>
        <errorlog>error.log</errorlog>
        <loglevel>2</loglevel> <!-- 4 Debug, 3 Info, 2 Warn, 1 Error -->
        <logsize>10000</logsize> <!-- Max size of a logfile -->
    </logging>
 
    <security>
        <chroot>0</chroot>
    </security>
</icecast>
svxreflector-vhost.conf
[GLOBAL]
TIMESTAMP_FORMAT="%Y-%m-%dT%H:%M:%S%z"
LISTEN_PORT=CHANGEME_REFL_PORT
CODECS=OPUS
 
[USERS]
 
[PASSWORDS]
DefaultPassword="CHANGEME_REFL_PASS"
svxlink-vhost.conf
[GLOBAL]
LOGICS=SimplexLogic,ReflectorLogic
CFG_DIR=/etc/svxlink/svxlink.d
TIMESTAMP_FORMAT="%c"
CARD_SAMPLE_RATE=48000
LINKS=ALLlink
 
[SimplexLogic]
TYPE=Simplex
RX=NONE
TX=TxStream
CALLSIGN=RESEAU_CHANGEME_VHOSTNAME
EVENT_HANDLER=/usr/share/svxlink/events.tcl
DEFAULT_LANG=en_US
 
[ReflectorLogic]
TYPE=Reflector
HOST=127.0.0.1
PORT=CHANGEME_REFL_PORT
CALLSIGN="RESEAU_CHANGEME_VHOSTNAME"
AUTH_KEY="CHANGEME_REFL_PASS"
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:CHANGEME_AIFACE_NUM,0
AUDIO_CHANNEL=0
PTT_TYPE=NONE
TIMEOUT=7200
TX_DELAY=0
PREEMPHASIS=0

Logrotate

/var/log/svxlink/svx-*.log {
    missingok
    notifempty
    weekly
    rotate 3
    create 0644 svxlink daemon
    postrotate
        killall -HUP svxreflector
    endscript
}
 
/var/log/svxlink/svxlink-*.log {
    missingok
    notifempty
    weekly
    rotate 3
    create 0644 svxlink daemon
    postrotate
        killall -HUP svxreflector
    endscript
}

Structure /var/svx