Skip to content

OpenVPN ATA Tunnels (Grandstream FXS gateways)

Per-device OpenVPN tunnels that give Grandstream GXW-series analog (FXS) gateways a stable, encrypted path into Astradial's cloud Asterisk — the OpenVPN counterpart of the WireGuard Customer Tunnels.

Same security model (per-customer isolation, SIP/RTP-only INPUT scope), different transport: the GXW gateways' firmware speaks OpenVPN, not WireGuard, so they cannot use wg1.

Status: Live · Server: 147.93.168.216 (cloud Asterisk host) · First customer: Mithra Scan (2026-06-15)

Why this exists (and why not WireGuard)

Grandstream GXW4216V2 (and siblings) are analog telephone adapters: they take physical phone/fax lines on FXS ports and register them to a PBX as SIP endpoints. To reach our cloud Asterisk reliably from a customer site (CGNAT, dynamic IP, NAT pinhole churn — the same problems described in Customer Tunnels), the device needs a stable tunnel.

The GXW firmware (1.0.25.2) ships an OpenVPN client only — no WireGuard. So these devices get their own OpenVPN transport (tun0) that runs in parallel with the WireGuard customer transport (wg1), governed by the same isolation rules.

The device firmware also constrains the crypto (this is why the server config looks "legacy"):

Constraint Required server setting
No tls-auth / tls-crypt (no ta.key field in UI) Omit them entirely
CBC only (no GCM in the dropdown) data-ciphers AES-256-CBC, data-ciphers-fallback AES-256-CBC
Plain digest auth SHA256
LZO toggle only Compression disabled on both sides (ATA "Enable LZO Compression = No") — avoids comp-lzo framing incompatibility on OpenVPN 2.6 and the VORACLE risk
UDP / TUN / 1194 proto udp, dev tun, port 1194
Key password Client key generated nopass → GXW "Client Key Password" left blank

Security model (read this first)

ATA tunnels carry external customer traffic and are isolated exactly like the WireGuard customer tunnels.

What enforces isolation

Layer Mechanism Effect
1. Per-device certificate One cert/key per physical device, never shared (duplicate-cn off) A stolen/cloned device can't impersonate another; revoke one without touching others
2. Separate interface + port tun0 on udp/1194, distinct from wg0/wg1 Per-interface firewall + audit
3. No client-to-client Directive deliberately absent OpenVPN won't bridge peers internally — all peer↔peer packets must pass the kernel firewall, where they're dropped
4. iptables FORWARD drops tun0↔tun0, tun0↔wg0, tun0↔wg1 all explicit DROP (+ default FORWARD policy DROP) A device cannot reach another ATA customer, a WireGuard customer, or internal infra (NUC/staging)
5. iptables INPUT scope tun0 traffic accepted only to SIP 5060/5080 + RTP 10000-20000; catch-all DROP for the rest Device can't reach AstraPBX API (8000), editor (3001), MariaDB (3306), etc.
6. Tunnel bypasses the public SIP geo-filter The india_sip ipset DROP is scoped -i eth0 only The device registers through the tunnel by certificate — it never has to be in the public geo-allowlist, and the public SIP attack surface doesn't grow
7. Split tunnel No redirect-gateway, no pushed routes The device can reach only 10.21.0.1 (the PBX). Its internet traffic does NOT flow through us

What a device CAN reach via its tunnel

✅ 10.21.0.1 (cloud Asterisk tunnel IP) on 5060/UDP, 5080/UDP, 10000-20000/UDP (RTP)

What a device CANNOT reach

❌ Other ATA devices            — tun0↔tun0 FORWARD DROP + no client-to-client
❌ WireGuard customers (wg1)     — tun0↔wg1 FORWARD DROP
❌ Internal infra (wg0: NUC .2, staging .3) — tun0↔wg0 FORWARD DROP
❌ AstraPBX API (8000) / editor (3001) / DB — tun0 INPUT catch-all DROP
❌ The public internet via us     — split tunnel (no redirect-gateway)

Known parity gap (same as wg1): host ports that UFW allows globally — SSH (22), 80/443, WSS (8089) — are accepted before the per-interface catch-all DROP, so a compromised device could in principle reach them on 10.21.0.1. This matches the existing WireGuard-tunnel posture. To close it for both transports, insert interface-scoped DROPs above the UFW jump (e.g. iptables -I INPUT <n> -i tun0 -p tcp --dport 22 -j DROP). Tracked as future hardening, not done by default to stay consistent with the wg1 model.

Architecture

Address allocation

Range Use Notes
10.10.10.0/24 Internal infra (wg0) EXISTING. Do not add customers here.
10.20.0.0/16 WireGuard customer tunnels (wg1) EXISTING.
10.21.0.0/24 OpenVPN ATA tunnels (tun0) NEW. .1 = cloud/PBX tunnel IP. .200–.250 = dynamic fallback pool. Statics .16–.199.
10.21.0.N/28 Per-customer block Each customer gets a /28; devices are static IPs within it (assigned via ccd).

When the first /24 fills (16 customers), add 10.21.1.0/24 as a second shard (new server/tun instance) or widen the existing subnet — see the runbook.

Device & customer registry

Customer Block Device Cert (CN) Tunnel IP Use Status
Mithra Scan 10.21.0.16/28 GXW4216V2 mithra-gxw01 10.21.0.17 Remote mgmt access Live 2026-06-15 (tunnel up, web UI reachable over tunnel)
Paavai Hospital 10.21.0.32/28 HT814 paavai-ht814 10.21.0.33 Remote mgmt access Live 2026-06-17 (tunnel up, web UI reachable over tunnel; HT814 drops ICMP — verify via HTTP, not ping)
Gopi Hospital 10.21.0.48/28 HT814 gopi-ht814 10.21.0.49 Remote mgmt access 🟡 Provisioned 2026-06-20 (cert + ccd + files staged; awaiting device-side upload/connect)
ACR Hospital 10.21.0.64/28 GXW4216V2 acr-gxw01 10.21.0.65 Remote mgmt access 🟡 Provisioned 2026-06-24 (cert + ccd + files staged)
DCNet 10.21.0.80/28 Dinstar DAG (FXS) dcnet-dag01 10.21.0.81 Remote mgmt access Live 2026-06-30 (tunnel up, device connected; web UI reachable over tunnel on HTTPS/443 — not HTTP/80)

Keep this table current — it is the human-readable source of truth for IP/cert allocation. The machine truth is /etc/openvpn/ccd/ (IPs) and /etc/openvpn/easy-rsa/pki/index.txt (certs) on the server.

On-server layout (147.93.168.216)

Path What
/etc/openvpn/server/astradial-ata.conf Server config (service openvpn-server@astradial-ata)
/etc/openvpn/easy-rsa/pki/ PKI — ca.crt, issued/, private/, dh.pem (RSA-2048, SHA256; CA 10 yr, client certs ~825 d)
/etc/openvpn/ccd/<device> Per-device static tunnel IP (ifconfig-push)
/etc/openvpn/clients/<customer>/ Staged upload files per customer (root 0600)
/usr/local/sbin/ovpn-add-client.sh Provisioning helper (cert + ccd + staged files)
/usr/local/sbin/openvpn-customer-iptables.sh Isolation rules — sibling of customer-tunnels-iptables.sh; called by systemd ExecStartPost/ExecStopPost
/var/log/openvpn/astradial-ata{.log,-status.log} Server log + connected-client status

Components vs the WireGuard tunnels

WireGuard customer tunnels OpenVPN ATA tunnels
Interface / port wg1 / udp 51821 tun0 / udp 1194
Subnet 10.20.0.0/16 (/30 per customer) 10.21.0.0/24 (/28 per customer)
Identity WG keypair + PSK X.509 cert per device
Isolation script customer-tunnels-iptables.sh (wg-quick PostUp/Down) openvpn-customer-iptables.sh (systemd ExecStartPost/StopPost)
Managed by Editor / API (DB-backed) CLI helper ovpn-add-client.sh + this registry
Use case Site routers (GWN), full PBX/site Single analog-gateway appliances (GXW FXS)

Operational notes & safety

  • Additive & call-safe. Standing this up touched nothing in the call path: no Asterisk restart/reload, no pjsip change, no wg change. Every firewall rule is scoped to -i tun0 / udp 1194.
  • Never ufw reload casually. Both isolation scripts add their rules with raw iptables outside UFW's managed chains (the wg1 rules likewise). A ufw reload flushes and rebuilds the chains, transiently dropping the live wg1 and tun0 customer rules until each service/interface re-triggers its script. If you must reload UFW, re-run both isolation scripts (or bounce wg1 and openvpn-server@astradial-ata) immediately after.
  • The OpenVPN port is opened by the isolation script (inserted at the top of INPUT), not by ufw allow — same reason as above. It self-heals on service start / reboot.

See also