Skip to content

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

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:

{customer_id}:{call_id}:{call_date}:{timestamp}:{signature}
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:

  1. Format check: Token must contain exactly 5 colon-separated parts
  2. Signature verification: Recomputes the HMAC and compares using constant-time comparison (hmac.compare_digest) to prevent timing attacks
  3. Expiry check: current_time - timestamp must be less than SIPVAULT_HMAC_MAX_AGE (default: 3600 seconds)
  4. Customer isolation: The customer_id from 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:

# /etc/sipvault/api.env
SIPVAULT_HMAC_MAX_AGE=7200   # 2 hours

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_SECRET in /etc/sipvault/api.env exactly

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:

  1. Navigate to R2 > Manage R2 API Tokens
  2. For the server token:
  3. Permissions: Object Read & Write
  4. Scope: Specific customer buckets only
  5. For the API token:
  6. Permissions: Object Read
  7. Scope: All customer buckets (for multi-tenant access)

Per-Customer Buckets

Each customer's data is stored in a separate R2 bucket:

sipvault-{customer_id}-{region}

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:

-rw-r----- root sipvault /etc/sipvault/server.env
-rw-r----- root sipvault /etc/sipvault/api.env

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:

sipvault-acme-us/       --> Customer "acme" data only
sipvault-globex-eu/     --> Customer "globex" data only

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

  1. The CDR Viewer computes call_hash = SHA-256(call_id)[:16] (first 16 hex characters)
  2. The dashboard URL uses this hash: https://sipvault.example.com/call/{call_hash}
  3. The full call_id is carried inside the HMAC token (not in the URL path)
  4. The API extracts the call_id from the validated token, computes its hash, and verifies it matches the URL path
  5. The call_id is 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:

sudo setcap cap_bpf,cap_net_admin,cap_sys_ptrace=eip /usr/local/bin/sipvault-agent
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:

sudo setcap cap_net_raw=eip /usr/local/bin/sipvault-agent
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