# SvxPortal web — installation HOWTO This is the **web front-end only** for a SvxReflector: node map, monitor, last-heard, talkgroup list, user accounts and the reflector admin pages. It is plain HTML/JavaScript plus a small PHP backend that stores accounts in Redis. No build step, no framework, no composer. What you need on the server: | Component | Why | |-----------|-----| | nginx | serves the static files and passes `*.php` to PHP-FPM | | PHP 8.2+ with FPM | the `auth/` backend (login, accounts, admin API) | | PHP extensions: `redis`, `mbstring`, `openssl`, `json`, `session` | used by `auth/lib.php` | | Redis 6+ | stores accounts, sessions, bans, talkgroup requests | | A way to send e-mail | signup verification and password reset (local MTA **or** an SMTP relay, see step 5) | | TLS certificate | the session cookie is `Secure`; login does not work over plain HTTP | Everything below assumes Debian 12/13 or Ubuntu 24.04, PHP 8.4 and that the site will live at `https://svxportal.example.com`. Replace the host name everywhere you see it. --- ## 1. Packages ```bash sudo apt install nginx php8.4-fpm php8.4-redis php8.4-mbstring php8.4-cli redis-server certbot ``` Check the PHP version you actually got (`php -v`) and use that number instead of `8.4` in the file names below. ## 2. Copy the web files ```bash sudo mkdir -p /var/www/html/svxportal sudo rsync -a --exclude .git --exclude install --exclude INSTALL.md ./ /var/www/html/svxportal/ sudo chown -R www-data:www-data /var/www/html/svxportal sudo find /var/www/html/svxportal -type d -exec chmod 2775 {} + sudo find /var/www/html/svxportal -type f -exec chmod 0644 {} + ``` ## 3. Redis (dedicated instance, unix socket) ```bash sudo cp install/redis-svxportal.conf /etc/redis/redis-svxportal.conf sudo mkdir -p /var/lib/redis-svxportal sudo chown redis:redis /var/lib/redis-svxportal /etc/redis/redis-svxportal.conf sudo chmod 640 /etc/redis/redis-svxportal.conf ``` Generate a password and put it in the config: ```bash openssl rand -hex 32 sudo sed -i "s/^requirepass .*/requirepass PASTE-THE-VALUE-HERE/" /etc/redis/redis-svxportal.conf sudo systemctl enable --now redis-server@svxportal ``` The socket is created with mode 770 and group `redis`, so the web user must be in that group: ```bash sudo usermod -aG redis www-data ``` Test: ```bash redis-cli -s /run/redis-svxportal/redis.sock -a 'PASTE-THE-VALUE-HERE' ping # -> PONG ``` ## 4. PHP-FPM Two pools are used. The default pool (`www`) handles all normal pages. A second small pool handles only `auth/admin_reflector_api.php`, because that script calls `exec()` to reach the sudo helpers (step 9) and you do not want `exec()` enabled for the whole site. ```bash sudo cp install/php-fpm-svxadmin.conf /etc/php/8.4/fpm/pool.d/svxadmin.conf ``` In the default pool hardening is recommended. Edit `/etc/php/8.4/fpm/pool.d/www.conf` and add at the end: ```ini php_admin_value[disable_functions] = exec,passthru,shell_exec,system,popen,proc_open,proc_close,pcntl_exec,dl,phpinfo,show_source php_admin_value[expose_php] = Off ``` Then: ```bash sudo systemctl restart php8.4-fpm ls -l /run/php/ # both php8.4-fpm.sock and php8.4-fpm-svxadmin.sock must exist ``` ## 5. Web app config (`auth/config.php`) ```bash cd /var/www/html/svxportal sudo -u www-data cp auth/config.php.example auth/config.php sudo chmod 640 auth/config.php sudoedit auth/config.php ``` Set: - `redis.unix_socket` -> `/run/redis-svxportal/redis.sock` - `redis.password` -> the value from step 3 - `base_url` -> `https://svxportal.example.com` (used in the e-mail links) - `mail.from` -> the sender address for verification / reset mails E-mail: with only `mail.from` set, PHP's `mail()` is used, which needs a working local MTA (`/usr/sbin/sendmail`, e.g. `msmtp-mta` or `postfix`). If you do not have one, add an SMTP relay instead and no MTA is needed: ```php 'mail' => [ 'from' => 'no-reply@example.com', 'from_name' => 'SvxPortal', 'host' => 'smtp.example.com', // SSL SMTP (implicit TLS) 'port' => 465, 'user' => 'smtp-user', 'pass' => 'smtp-password', ], ``` `config.php` is listed in `.gitignore` and is never served by nginx (all `*.php` go to PHP-FPM, which just executes `return [...]`). Keep it mode 640. ## 6. Talkgroup list file The talkgroup list is a JSON file the portal reads and writes: ```bash sudo cp install/tmpfiles-svxportal-tglist.conf /etc/tmpfiles.d/svxportal-tglist.conf sudo systemd-tmpfiles --create /etc/tmpfiles.d/svxportal-tglist.conf ``` This creates `/etc/svxlink/tglist.json` owned by `www-data`. The path is `tglist_file` in `config.php`; change both if you want it elsewhere. ## 7. nginx + TLS ```bash sudo mkdir -p /var/www/letsencrypt sudo cp install/nginx-svxportal.conf /etc/nginx/sites-available/svxportal sudo sed -i 's/svxportal\.example\.com/YOUR.HOST.NAME/g' /etc/nginx/sites-available/svxportal ``` Get a certificate first (the config references it): ```bash sudo certbot certonly --webroot -w /var/www/letsencrypt -d YOUR.HOST.NAME ``` If certbot has not created `/etc/letsencrypt/options-ssl-nginx.conf` and `ssl-dhparams.pem`, run `sudo certbot --nginx` once or remove those two lines. ```bash sudo ln -s /etc/nginx/sites-available/svxportal /etc/nginx/sites-enabled/ sudo nginx -t && sudo systemctl reload nginx ``` Open `https://YOUR.HOST.NAME/` — the page should render. Click **Sign in** and you should get a proper "wrong password" style error, not a 500. A 500 here almost always means Redis (wrong socket/password, or `www-data` not in group `redis`). Also edit these files in the web root for your own site name: `robots.txt`, `sitemap.xml` and the `