The default VICIdial install serves recordings from /RECORDINGS/ over plain HTTP with directory listing available and no authentication. Anyone who guesses the path can browse and download every call your business has ever made.
Those recordings contain names, addresses, account numbers, and in far too many cases card details read aloud. This is the highest-value target on the box, and it is usually the least protected part of it.
Step 1: Close the Directory
Find the vhost. On ViciBox it is under /etc/apache2/vhosts.d/; on a CentOS or AlmaLinux scratch install, typically /etc/httpd/conf.d/.
Disable indexing and require authentication:
<Directory /srv/www/htdocs/RECORDINGS>
Options -Indexes -FollowSymLinks
AllowOverride None
AuthType Basic
AuthName "Call Recordings"
AuthUserFile /etc/apache2/recordings.passwd
Require valid-user
</Directory>
Create the password file:
htpasswd -c /etc/apache2/recordings.passwd qa_supervisor
htpasswd /etc/apache2/recordings.passwd compliance
The -c flag creates the file, so use it only the first time. Using it again wipes the existing users.
apachectl configtest
systemctl reload apache2
Then update Admin → System Settings → Recording Web Link so playback links from within VICIdial still resolve correctly.
Restrict by Address Too
Authentication plus network restriction is better than either alone:
<Directory /srv/www/htdocs/RECORDINGS>
Options -Indexes
AuthType Basic
AuthName "Call Recordings"
AuthUserFile /etc/apache2/recordings.passwd
Require valid-user
Require ip 203.0.113.0/24
</Directory>
With both directives present, Apache 2.4 defaults to requiring only one to pass. To require both:
<RequireAll>
Require valid-user
Require ip 203.0.113.0/24
</RequireAll>
Step 2: Force HTTPS
Basic auth over HTTP sends credentials in base64, which is encoding rather than encryption. Anyone on the path reads them.
<VirtualHost *:80>
ServerName dialer.example.com
Redirect permanent / https://dialer.example.com/
</VirtualHost>
If you have not yet set up a certificate, acme.sh or the ViciBox SSL wrapper handles it in a couple of commands. There is no scenario in 2022 where recordings should be served unencrypted in transit.
Step 3: Change the Path
/RECORDINGS/ is documented everywhere. Rename it:
Alias /rz7-media /srv/www/htdocs/RECORDINGS
Obscurity is not security, but it removes you from automated scans that only check known paths. Update the Recording Web Link setting to match.
Step 4: Filesystem Permissions
Asterisk writes recordings; Apache reads them. Nothing else should touch them.
chown -R asterisk:apache /var/spool/asterisk/monitorDONE
chmod -R 640 /var/spool/asterisk/monitorDONE
chmod 750 /var/spool/asterisk/monitorDONE
Check what the directory actually looks like — a world-readable recordings folder on a box with multiple shell users is an internal breach waiting to happen:
ls -ld /var/spool/asterisk/monitorDONE
find /var/spool/asterisk/monitorDONE -perm /o+r | head
Step 5: Encryption at Rest
Filesystem permissions protect against other users on the box. They do nothing if a disk is removed, a VM image is copied, or a backup is exfiltrated.
Encrypted Partition
The cleanest approach is a LUKS volume for recordings, set up before the system goes into production:
cryptsetup luksFormat /dev/sdb1
cryptsetup luksOpen /dev/sdb1 recordings
mkfs.ext4 /dev/mapper/recordings
mount /dev/mapper/recordings /var/spool/asterisk/monitorDONE
The trade-off is that the volume needs unlocking after every reboot. On a server that must come back unattended, a keyfile on the root filesystem defeats much of the point — you are protecting against physical disk theft, not against someone with root.
Decide which threat you are actually defending against before choosing.
Encrypting Archived Files
For recordings moved off to long-term storage, encrypt at the file level:
#!/bin/bash
SRC=/var/spool/asterisk/monitorDONE
DST=/archive/recordings
DAY=$(date -d yesterday +%Y-%m-%d)
find "$SRC" -name "*${DAY}*" -type f | while read -r f; do
gpg --batch --yes --trust-model always \
--encrypt --recipient archive@example.com \
--output "${DST}/$(basename "$f").gpg" "$f"
done
Keep the private key off this server. An encrypted archive whose decryption key sits in the same directory is not an archive, it is a formality.
Step 6: Retention That Actually Deletes
The strongest control over old recordings is not having them. Most regulatory regimes set a minimum retention, not a mandate to keep forever, and every month you hold beyond your policy is a month of additional exposure.
#!/bin/bash
# Delete recordings older than the retention window
RETAIN_DAYS=365
find /var/spool/asterisk/monitorDONE -type f -name "*.mp3" -mtime +${RETAIN_DAYS} -delete
find /var/spool/asterisk/monitorDONE -type f -name "*.wav" -mtime +${RETAIN_DAYS} -delete
Clean the database rows to match, or your reports will link to files that no longer exist:
DELETE FROM recording_log
WHERE start_time < NOW() - INTERVAL 365 DAY;
Test the find with -print before you put -delete on it. Run it from cron, and verify a month later that it is actually running — retention policies that exist only in a document are the norm rather than the exception.
Step 7: Card Data
If agents take payments over the phone, recording the card number and CVV creates a PCI DSS problem that no amount of file permissions solves.
The standard answer is pause-and-resume: the agent pauses recording before the card is read and resumes afterwards. VICIdial supports this through the agent interface, and it can be triggered from the dialplan:
exten => *5,1,AGI(agi://127.0.0.1:4577/call_log--PAUSE)
exten => *6,1,AGI(agi://127.0.0.1:4577/call_log--RESUME)
Better still is routing the payment portion to an IVR where the customer keys the number themselves and the digits never enter the recording or reach the agent.
CVV must never be stored after authorisation under any circumstances. If it is currently in your recordings, that is a live compliance problem rather than a project for next quarter.
Audit
Know who has been accessing recordings:
grep "rz7-media" /var/log/apache2/access_log | awk '{print $1, $7}' | sort | uniq -c | sort -rn | head -20
Unexpected source addresses, or one account pulling hundreds of files, are worth investigating the same day.
Checklist
- Directory listing disabled
- Authentication required, over HTTPS only
- Default path renamed
- Filesystem permissions restricted to asterisk and apache
- Encryption decision made deliberately, with the key stored elsewhere
- Retention job running and verified
- Card data excluded from recordings entirely
- Access logs reviewed on a schedule