SIP VAULT Security Guide
This guide covers the security architecture, authentication mechanisms, network security, data protection, and tenant isolation in SIP VAULT.
Table of Contents
- Security Architecture Overview
- Authentication: HMAC-SHA256 Signed Tokens
- CDR Viewer Token Generation
- S3/R2 Credentials Management
- Network Security
- Firewall Recommendations
- Customer Isolation
- Hash-Based Call-ID Verification
- Data at Rest Protection
- Retention Policy as Security Control
- Attack Surface Analysis
- Agent Security
Security Architecture Overview
SIP VAULT follows a minimal-surface security design:
- No user accounts or passwords -- authentication is exclusively through HMAC-SHA256 signed tokens generated by the CDR Viewer
- No database -- eliminates SQL injection, ORM vulnerabilities, and database credential management
- Per-customer S3 buckets -- tenant data isolation at the storage layer
- Short-lived tokens -- default 1-hour expiry, configurable via
SIPVAULT_HMAC_MAX_AGE - Pre-shared secrets -- agent-to-server authentication uses per-customer tokens configured on both sides
The trust model:
CDR Viewer (trusted) --> generates HMAC token --> user's browser
User's browser --> presents token --> SIP VAULT API --> validates HMAC --> serves data
Agent (trusted) --> authenticates with token --> SIP VAULT server --> writes to S3
Authentication: HMAC-SHA256 Signed Tokens
All dashboard access is authenticated via HMAC-SHA256 signed tokens. There is no login form, no session management, and no user database.
Token Format
The token is a colon-separated string with five components:
| Component | Description | Example |
|---|---|---|
customer_id |
Customer identifier | acme |
call_id |
The SIP Call-ID header value | a7a186b1ff8c4cfeaa90a9144ba9cb24 |
call_date |
Call date in YYYY/MM/DD format |
2026/03/14 |
timestamp |
Unix timestamp when the token was created | 1710403200 |
signature |
HMAC-SHA256 hex digest of the payload | e3b0c44298fc1c14... |
Signature Computation
The signature is computed over the payload (the first four components joined by colons):
payload = "{customer_id}:{call_id}:{call_date}:{timestamp}"
signature = HMAC-SHA256(secret, payload)
The secret is the SIPVAULT_HMAC_SECRET value configured in api.env.
Token Validation
The API validates tokens on every request via the require_token dependency:
- Format check: Token must contain exactly 5 colon-separated parts
- Signature verification: Recomputes the HMAC and compares using constant-time comparison (
hmac.compare_digest) to prevent timing attacks - Expiry check:
current_time - timestampmust be less thanSIPVAULT_HMAC_MAX_AGE(default: 3600 seconds) - Customer isolation: The
customer_idfrom the token determines which S3 bucket to query
If any check fails, the API returns HTTP 401 with a specific error:
- invalid token format -- wrong number of components
- invalid timestamp -- timestamp is not a valid integer
- invalid signature -- HMAC does not match
- token expired -- token age exceeds max age
Token Expiry Configuration
The default token lifetime is 1 hour (3600 seconds). To change it:
For security, keep the expiry as short as practical. The token is embedded in the dashboard URL, so it remains valid for the duration of the user's browsing session as long as it hasn't expired.
CDR Viewer Token Generation
Tokens are generated server-side by the CDR Viewer application (typically OpenSIPS Control Panel). The HMAC secret never reaches the end user's browser -- only the signed token does.
Token Generation Flow
1. User clicks "SIP VAULT" in CDR Viewer
2. CDR Viewer server-side code:
a. Gets call_id and call_date from the CDR record
b. Computes call_hash = SHA-256(call_id)[:16]
c. Builds payload = "{customer_id}:{call_id}:{call_date}:{now}"
d. Signs: signature = HMAC-SHA256(secret, payload)
e. Builds token = "{payload}:{signature}"
f. Returns URL: https://sipvault.example.com/call/{call_hash}?token={token}
3. User's browser opens the URL
4. Dashboard SPA extracts the token from the query string
5. All API requests include the token as a query parameter
6. API validates the token on every request
Security Requirements for Token Generators
- The HMAC secret must be stored securely on the CDR Viewer server, not in client-side code
- Token generation must happen server-side only
- The secret in the generator must match
SIPVAULT_HMAC_SECRETin/etc/sipvault/api.envexactly
S3/R2 Credentials Management
SIP VAULT uses two separate sets of S3 credentials with different privilege levels.
Credential Separation
| Credential Set | Used By | Permissions | Configured In |
|---|---|---|---|
| Server credentials | sipvault-server | Object Read & Write | /etc/sipvault/server.env (SIPVAULT_S3_ACCESS_KEY, SIPVAULT_S3_SECRET_KEY) |
| API credentials | FastAPI | Object Read only | /etc/sipvault/api.env (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY) |
Least-Privilege Principle
- The server needs read/write access to store call data
- The API only needs read access to generate pre-signed URLs for the dashboard
- Neither credential set should have bucket deletion or management permissions
Creating R2 API Tokens
In the Cloudflare dashboard:
- Navigate to R2 > Manage R2 API Tokens
- For the server token:
- Permissions: Object Read & Write
- Scope: Specific customer buckets only
- For the API token:
- Permissions: Object Read
- Scope: All customer buckets (for multi-tenant access)
Per-Customer Buckets
Each customer's data is stored in a separate R2 bucket:
This provides storage-level tenant isolation. Even if a token is compromised, it only grants access to that customer's bucket.
File Permissions
Environment files containing credentials are protected at the filesystem level:
Only root can write them; only the sipvault group can read them. The sipvault system user (which runs the services) is a member of this group.
Network Security
Port Map
| Port | Protocol | Direction | Purpose | Access |
|---|---|---|---|---|
| 443 | TCP | Inbound | HTTPS (dashboard + API) | Public |
| 80 | TCP | Inbound | HTTP (redirects to HTTPS) | Public |
| 9060 | TCP | Inbound | Agent-to-server data | Restricted to customer IPs |
| 9060 | UDP | Inbound | HEP v3 RTCP data | Restricted to customer IPs |
| 8000 | TCP | Loopback only | FastAPI (proxied by nginx) | localhost only |
TLS for Dashboard and API
All user-facing traffic is encrypted with TLS 1.2+: - Certificates are managed by Let's Encrypt via certbot - nginx handles TLS termination - HTTP is redirected to HTTPS with a 301 response
Agent-to-Server Connections
Agents connect to the server on TCP port 9060. The connection:
- Uses a custom binary protocol over TCP
- Authenticates using the per-customer token configured in the agent's agent.conf and the server's SIPVAULT_CUSTOMERS
- Should be restricted to known customer IP ranges at the firewall level
HEP v3 RTCP
When RTPProxy runs on a separate machine, RTCP data is sent via HEP v3 over UDP port 9060: - Source: RTPProxy/media server - Destination: SIP VAULT server - Should be restricted to known media server IPs at the firewall level
Internal Service Communication
The FastAPI service listens only on 127.0.0.1:8000:
- Not accessible from the network
- Proxied through nginx at /api/
- nginx adds X-Real-IP, X-Forwarded-For, and X-Forwarded-Proto headers
Firewall Recommendations
Server Firewall (ufw)
# Allow HTTPS (public)
sudo ufw allow 443/tcp comment "HTTPS"
# Allow HTTP redirect (public)
sudo ufw allow 80/tcp comment "HTTP redirect"
# Allow agent connections (restrict to customer IP ranges)
sudo ufw allow from 10.0.0.0/8 to any port 9060 proto tcp comment "SIP VAULT agent TCP"
# Allow HEP RTCP (restrict to media server IPs)
sudo ufw allow from 10.0.0.0/8 to any port 9060 proto udp comment "SIP VAULT HEP UDP"
# Allow SSH (restrict to admin IPs)
sudo ufw allow from ADMIN_IP to any port 22 proto tcp comment "SSH"
# Enable firewall
sudo ufw enable
For production deployments, replace 10.0.0.0/8 with specific customer IP addresses or CIDR ranges.
Customer Firewall (Agent Side)
The agent only needs outbound access:
# Allow outbound TCP to SIP VAULT server
sudo ufw allow out to SERVER_IP port 9060 proto tcp comment "SIP VAULT agent"
No inbound ports need to be opened for the agent.
Customer Isolation
SIP VAULT enforces tenant isolation at multiple layers:
1. Storage Isolation
Each customer has a dedicated S3/R2 bucket:
2. Token-Based Access Control
The HMAC token contains the customer_id, which determines which bucket the API queries:
Token: acme:call-id-123:2026/03/14:1710403200:signature
|
customer_id = "acme" --> bucket = "sipvault-acme-us"
A valid token for customer "acme" cannot be used to access customer "globex" data.
3. Agent Authentication
Each customer's agent authenticates with a unique token:
SIPVAULT_CUSTOMERS=[
{"id":"acme", "token":"token-for-acme", "bucket":"sipvault-acme-us"},
{"id":"globex", "token":"token-for-globex", "bucket":"sipvault-globex-eu"}
]
An agent with customer "acme" credentials can only write to the "acme" bucket.
4. URL Hash Isolation
Dashboard URLs use a SHA-256 hash of the Call-ID. Even if someone guesses a Call-ID hash, they still need a valid HMAC token with the correct customer_id and call_id to access the data.
Hash-Based Call-ID Verification
SIP Call-IDs can contain sensitive information (IP addresses, timestamps, etc.) and are often long and unwieldy for URLs. SIP VAULT uses SHA-256 hashing for URL construction.
How It Works
- The CDR Viewer computes
call_hash = SHA-256(call_id)[:16](first 16 hex characters) - The dashboard URL uses this hash:
https://sipvault.example.com/call/{call_hash} - The full
call_idis carried inside the HMAC token (not in the URL path) - The API extracts the
call_idfrom the validated token, computes its hash, and verifies it matches the URL path - The
call_idis then used to construct the S3 path for data retrieval
This means: - The raw Call-ID is never exposed in browser history or server access logs via the URL path - The URL hash alone cannot be used to access data without a valid token - An attacker with a URL hash but no token gets HTTP 401 - An attacker with a token but the wrong URL hash gets a mismatch error
Data at Rest Protection
GZIP Compression
All stored data is GZIP compressed:
- sip.pcap.gz -- SIP packet captures
- rtcp.json.gz -- Raw RTCP reports
- log-NNNNNN.gz -- OpenSIPS log chunks
The quality.json file is stored uncompressed for fast access by the API.
S3/R2 Encryption
Cloudflare R2 encrypts all objects at rest by default using AES-256. No additional configuration is needed.
Pre-Signed URLs
The API never serves S3 objects directly. Instead, it generates short-lived pre-signed URLs that grant temporary read access to specific objects. This means: - The API's S3 credentials are never exposed to the client - Pre-signed URLs expire after a short period - Each URL is scoped to a single S3 object
Retention Policy as Security Control
The data retention policy (/etc/sipvault/retention.yml) serves as a security control by limiting how long sensitive call data persists.
Default Retention Periods
| Data Type | Default | Contains |
|---|---|---|
| SIP PCAP | 7 days | Packet-level SIP traffic (may include auth headers) |
| RTCP Raw | 7 days | Network quality measurements, IP addresses |
| Quality JSON | 30 days | Aggregated quality analysis (less sensitive) |
| Log Chunks | 14 days | OpenSIPS logs (may contain SIP URIs, IPs) |
Security Rationale
- SIP PCAPs contain the most sensitive data (full packet captures) and have the shortest retention
- Quality JSON contains only aggregated statistics and can be kept longer
- Log chunks may contain SIP URIs and IP addresses, warranting moderate retention
- Per-customer overrides allow compliance with specific customer data retention requirements
Compliance Considerations
For regulated environments, the retention policy can be configured per-customer:
customers:
regulated_customer:
sip_pcap_days: 3 # Minimize PII retention
rtcp_raw_days: 3
quality_json_days: 90 # Keep quality data for SLA reporting
log_chunks_days: 7
Attack Surface Analysis
What SIP VAULT Does NOT Have
| Absent Component | Security Benefit |
|---|---|
| Database | No SQL injection, no ORM vulnerabilities, no DB credential management |
| User accounts | No password storage, no credential stuffing, no account takeover |
| Session management | No session hijacking, no CSRF (tokens are URL-based) |
| File uploads | No upload-based attacks |
| Admin panel | No admin credential compromise |
Remaining Attack Vectors
| Vector | Mitigation |
|---|---|
| HMAC secret compromise | Keep secret in /etc/sipvault/api.env (640 permissions). Rotate by changing the secret in both api.env and CDR Viewer config |
| S3 credential compromise | Use least-privilege tokens. Rotate via Cloudflare dashboard |
| Token replay (within expiry) | Keep SIPVAULT_HMAC_MAX_AGE short (default: 1 hour) |
| Agent token compromise | Rotate the token in both SIPVAULT_CUSTOMERS and the agent's agent.conf |
| Network eavesdropping on port 9060 | Restrict port 9060 to known customer IPs via firewall |
| nginx/FastAPI vulnerabilities | Keep packages updated, monitor CVEs |
Agent Security
Capability Requirements
The agent binary requires elevated privileges for packet capture, but does NOT need full root access:
eBPF mode:
| Capability | Purpose |
|---|---|
CAP_BPF |
Load and run eBPF programs |
CAP_NET_ADMIN |
Attach XDP/tc programs to network interfaces |
CAP_SYS_PTRACE |
Attach kprobes for log capture |
pcap mode:
| Capability | Purpose |
|---|---|
CAP_NET_RAW |
Open raw sockets for packet capture via libpcap |
Agent System User
The installer does not create a dedicated system user for the agent -- the systemd service runs as root (necessary for packet capture). On systems where capabilities are set on the binary, the service can be configured to run as a less-privileged user.
Disk Buffer Security
The agent stores buffered data at /var/lib/sipvault/buffer.dat (default, configurable). This file may contain captured SIP packets and log data. Ensure:
- The buffer directory has restrictive permissions:
chmod 750 /var/lib/sipvault - The buffer file is cleaned up when the agent is uninstalled
- The maximum buffer size (default: 100 MB) prevents disk exhaustion
What the Agent Captures
The agent captures ONLY: - INVITE dialogs -- no REGISTER, OPTIONS, SUBSCRIBE, or NOTIFY - RTCP packets on the configured RTP port range - OpenSIPS logs related to captured Call-IDs
It does NOT capture:
- RTP media (voice data)
- SIP messages outside of INVITE dialogs
- Traffic on ports not configured in sip_ports or rtp_port_min/rtp_port_max