You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Web/API runs as the unprivileged astergui user with NoNewPrivileges, ProtectSystem=strict, empty capability set. It cannot
write /etc/asterisk, has no sudo, and cannot read /etc/asterisk/webgui (mode 0750 asterisk).
Every privileged action goes through astergui-helper (root, stdlib only, ~500 lines). systemd owns the socket
/run/astergui-helper.sock (mode 0600, only the astergui user); the client can send only apply, rollback or
read-deployed, sip-cert-status, sync-media - no arguments cross the boundary, no sudoers rules. The only commands with an
argument are rec-get, rec-delete (a recording file name) and rec-prune (a number of days); each argument is matched against a fixed
pattern by the helper itself (YYYYMMDD-HHMMSS_<caller>_<dialled>_<id>.wav), so no path can be smuggled in.
The helper never trusts staged input: O_NOFOLLOW reads into memory, filename whitelist, size and line limits, no
#include/#exec, deny-list of code-running dialplan applications (System, Shell, AGI, FILE(), CURL() ...),
writes only inside /etc/asterisk/webgui/. Snapshots and state live in /var/lib/astergui-helper (root 0700).
Call flow, audio and recordings
Audio uploads are validated three times (browser conversion, API, root helper): uncompressed 16-bit mono PCM WAV at 8/16 kHz, at most
25 MB and 60 minutes each, names [a-z0-9_-]. The helper copies them from the staging area (no symlinks, no unexpected entries) into
asterisk-owned directories and only ever deletes *.wav/*.wav16 files; a staging area without index.json is refused, so an empty
stage can never wipe the library.
Recordings are readable, downloadable and deletable by administrators only. Playback is written to the audit log. The helper copies a
file to a random name in a directory only the web user can read (/var/lib/astergui/export, removed after streaming, purged after an hour).
Recordings are not included in astergui backups. Whether and how you may record calls is a legal matter for you.
Dialplan deny-list: besides System/Shell/AGI/FILE()/CURL(), the helper refuses MixMonitor with an absolute path, .. or a
third (shell command) argument, so a compromised web app cannot use recording to write arbitrary files or run commands.
Voicemail messages are administrator-only in the UI/API (list, play, download, delete); every play and delete is audited. The helper
takes a mailbox (2-8 digits), a fixed folder name and msgNNNN, refuses anything else, never follows symlinks, and copies the audio to the
same private export area as recordings.
Forwarding to external numbers can cost money and lets callers reach your outbound routes through an extension: only administrators can set it.
Packet capture: the web service never runs tcpdump. cap-start takes an interface name that must exist in /sys/class/net (or any), one of three presets,
an optional IP address, up to eight port numbers and two bounded limits; the BPF filter is assembled from those validated pieces (no free-form expression). The capture runs in its
own unit (astergui-capture.service: root but only CAP_NET_RAW, CAP_NET_ADMIN, CAP_CHOWN, CAP_DAC_OVERRIDE, CAP_FOWNER, read-only filesystem except the captures folder) and
re-validates the request file itself. Files are root:astergui 0640 in a folder owned by the web user, so listing, download and delete need no privileges. A compromised web
service could therefore read existing captures (as it can already reach the recording exports); keep captures short-lived and delete them after analysis.
The helper's systemd unit can write only the GUI-owned directories (/etc/asterisk/webgui, the audio and recordings folders, the export folder).
Secrets
SIP passwords: only md5_cred digests stored/rendered; plaintext shown once, cannot be retrieved, only regenerated.
(Trade-off: MD5 digest auth is what SIP uses anyway; a stolen digest still allows offline guessing, mitigated by 24-char random secrets.)
Voicemail PINs and future trunk credentials must be plaintext in Asterisk config; in the DB they are Fernet-encrypted
(key in /etc/astergui/astergui.env, 0640 root:astergui). Never returned by the API, never audited or logged.
Database uses peer authentication over a unix socket: no DB password exists. PostgreSQL listens on localhost only.
AMI: 127.0.0.1 only, permit restricted to loopback, minimal privileges, secret generated at install.
Web
Argon2id password hashes; server-side sessions (only a SHA-256 of the token is stored); cookie HttpOnly; Secure; SameSite=Lax.
CSRF: per-session token in X-CSRF-Token for all mutating requests + Origin check.
Login rate limiting per user and per IP (5 / 20 failures per 15 min, DB-backed, survives restarts); uniform error message and timing.
All input validated (extension numbers, names, codecs, e-mail, PIN); renderers refuse newlines/;/NUL a second time.
nginx sets CSP (default-src 'self'), X-Frame-Options: DENY, nosniff; HSTS is left commented until a trusted certificate is installed.
An optional plain-HTTP listener exists (astergui web http enable, off by default, flagged on the Security page). The Secure cookie flag follows the
scheme nginx reports (X-Forwarded-Proto); nginx always overwrites that header, and the app only listens on a unix socket.
Web phone: nginx exposes /ws (proxy to Asterisk's loopback-only HTTP server) but asks the astergui API first (auth_request), so only a signed-in
administrator can open the WebSocket; Asterisk still requires the SIP digest login of a web-phone extension. The credentials endpoint is administrator-only and every hand-over is audited.
The SIP secret of web-phone extensions is stored Fernet-encrypted (all other extensions keep only md5_cred), so a database dump plus the key file would reveal those secrets.
Use dedicated extensions for it.
The default TLS certificate is self-signed - replace /etc/astergui/tls/astergui.{crt,key}.
Accounts
Roles: administrator and read-only. Enforced on the server for every mutating request (a read-only session gets 403 even if the
UI is bypassed); user management and backups are administrator-only. Temporary passwords are shown once and must be replaced at first sign-in
(until then only the change-password call works). The last active administrator cannot be disabled, demoted or deleted; nobody can lock themselves out.
Changing or resetting a password signs the affected user out everywhere else. Sessions can be listed and revoked.
Password rules: >= 12 characters, not containing the username, not trivially repetitive; Argon2id hashing.
Hardening features
Per-extension network ACL (deny=0.0.0.0/0 + permit=...): registration and calls from other addresses are refused (Not match Endpoint ACL).
SIP over TLS, pinned to TLS 1.2 (PJSIP's default would be TLSv1), and SRTP (SDES) per extension; SRTP cannot be enabled without TLS.
Trunk contexts are isolated: inbound calls can only reach inbound routes, never dial out or reach extensions directly.
Click-to-call uses its own AMI user with only originate; the main AMI user has no originate, neither has system+originate together
(which would allow System() through AMI).
Backups are 0600, contain secrets, and are verified by checksum before a restore touches the database.
CDR export neutralises spreadsheet formula injection (cells starting with = + - @ other than plain phone numbers).
Security page (read-only) reports exposed listeners, firewall state, certificate expiry, missing backups, configuration drift and
well-known sample-config risks (e.g. the stock voicemail mailbox 1234 with PIN 4242). It proposes firewall rules but never applies them.
fail2ban filter/jail for repeated failed SIP logins are provided (opt-in, scripts/enable-fail2ban.sh).
Not covered yet
astergui never configures the firewall itself (by design). SIP (udp/5060) and RTP (udp/10000-20000) are open if the host has no firewall.
Sample IAX2/Skinny listeners of the stock Asterisk config are reported, not disabled.