10. External API & Security (Debian 13)
In the previous parts of this series, we successfully packaged and deployed the Ring2All core databases, Kamailio SBCs, GlusterFS clusters, and RTPEngine media nodes. In this tenth installment, we will focus on exposing and securing the Ring2All Fastify API Backend for external integrations (such as CRMs, custom ERP systems, or notification bots).
By default, the Ring2All API backend listens locally on port 3000/3001. We will deploy a secure Nginx Reverse Proxy on the Portal/Admin nodes to expose the API over HTTPS (port 443) and write custom NFTables filtering rules (the native packet filter framework in Debian 13) to restrict API access to trusted external source IPs.
🛠️ Step 1: Configuring Nginx Secure Reverse Proxy
Section titled “🛠️ Step 1: Configuring Nginx Secure Reverse Proxy”To proxy incoming external HTTPS requests to our internal API listener on port 3000, we configure Nginx with upstream proxy blocks and secure SSL/TLS protocols.
- Create a new Nginx virtual host configuration:
nano /etc/nginx/sites-available/ring2all-api.conf - Add the following upstream and server block config:
upstream ring2all_api {server 127.0.0.1:3000;keepalive 32;}server {listen 443 ssl http2;server_name pbx.softswitchone.com;# SSL Certificatesssl_certificate /etc/letsencrypt/live/pbx.softswitchone.com/fullchain.pem;ssl_certificate_key /etc/letsencrypt/live/pbx.softswitchone.com/privkey.pem;# Strong TLS configuration (Debian 13 default openssl 3.x)ssl_protocols TLSv1.2 TLSv1.3;ssl_prefer_server_ciphers on;ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';# Security Headersadd_header X-Frame-Options "DENY" always;add_header X-Content-Type-Options "nosniff" always;add_header X-XSS-Protection "1; mode=block" always;add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;# Loggingaccess_log /var/log/nginx/ring2all_api_access.log;error_log /var/log/nginx/ring2all_api_error.log warn;# Proxy to Fastify Backend APIlocation /api/ {proxy_pass http://ring2all_api/;proxy_http_version 1.1;# Keep-alive headersproxy_set_header Connection "";# Real IP headersproxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto $scheme;proxy_set_header X-Forwarded-Host $host;proxy_set_header X-Forwarded-Port $server_port;# Timeoutsproxy_connect_timeout 60s;proxy_send_timeout 60s;proxy_read_timeout 60s;# Max upload limit (e.g. for CSV bulk provisioning imports)client_max_body_size 100M;}}
- Enable the configuration and test Nginx:
Terminal window ln -s /etc/nginx/sites-available/ring2all-api.conf /etc/nginx/sites-enabled/nginx -tsystemctl reload nginx
🔒 Step 2: Securing Access with NFTables (Debian 13 Native)
Section titled “🔒 Step 2: Securing Access with NFTables (Debian 13 Native)”Debian 13 utilizes nftables as the default packet filtering framework, replacing the legacy iptables backend. We will define an address set representing authorized CRM and external developer IPs, allowing traffic to port 443 only from those addresses while dropping all other requests.
- Open the nftables configuration file:
nano /etc/nftables.conf - Update the input chain rules to define a set of allowed external IPs. Replace the configuration with the following structure:
#!/usr/sbin/nft -fflush rulesettable inet filter {# Set of authorized external IPs (CRMs, third-party developers, webhooks)set authorized_api_ips {type ipv4_addrflags intervalelements = {192.168.1.50, # Corporate Office IP76.13.102.153, # Dev Server IP104.16.0.0/12 # CRM Provider / HubSpot webhooks IP block example}}chain input {type filter hook input priority filter; policy drop;# Allow loopback interfaceiif "lo" accept# Allow established/related state connectionsct state established,related accept# Drop invalid connectionsct state invalid drop# Allow SSH (Port 22) - Restrict to Admin subnet in productiontcp dport 22 accept# Allow HTTP (Port 80) for Let's Encrypt SSL verification challengestcp dport 80 accept# Allow HTTPS (Port 443) only from the authorized setsip saddr @authorized_api_ips tcp dport 443 accept# Logging and dropping unauthorized packetslog prefix "NFTABLES-INPUT-BLOCKED: " flags all counter drop}chain forward {type filter hook forward priority filter; policy drop;}chain output {type filter hook output priority filter; policy accept;}}
- Validate and load the new nftables rules:
Terminal window # Test rule compilation syntaxnft -c -f /etc/nftables.conf# Reload servicesystemctl restart nftablessystemctl enable nftables
🧪 Step 3: Verification
Section titled “🧪 Step 3: Verification”Once Nginx and NFTables are loaded on your Debian 13 portal server:
1. Test from an Unauthorized IP Address
Section titled “1. Test from an Unauthorized IP Address”Attempting to connect to the API from an IP not listed inside the authorized_api_ips set will block connection negotiations:
curl -I -v https://pbx.softswitchone.com/api/telephony/extensions# Output should timeout or report "Connection refused/timed out"2. Test from an Authorized IP Address
Section titled “2. Test from an Authorized IP Address”Connecting from a whitelisted IP with the correct API Key header will query the platform successfully:
curl -i -H "X-API-Key: ak_your_key" \ https://pbx.softswitchone.com/api/telephony/extensions# Output: HTTP/1.1 200 OK3. Check NFTables Block Logs
Section titled “3. Check NFTables Block Logs”You can monitor blocked attempts in real-time in the system log:
journalctl -k -f | grep "NFTABLES-INPUT-BLOCKED"This concludes Part 10 of our series. Exposing your Ring2All cluster to external CRM integrations is now completely locked down and secure.

