Skip to content

Provision a Grandstream ATA onto the VPN

Step-by-step for putting a Grandstream GXW-series FXS gateway (e.g. GXW4216V2) onto the OpenVPN ATA tunnel. Worked example: Mithra Scan (mithra-gxw0110.21.0.17), provisioned 2026-06-15.

Architecture, security model, and the IP/cert registry live in the feature doc. This page is the "how to do it" runbook.

Prerequisites

  • SSH to the cloud host: ssh root@147.93.168.216
  • The OpenVPN server is already up (one-time; see feature doc). Check: systemctl is-active openvpn-server@astradial-ataactive.
  • A free /28 and tunnel IP picked from the registry. Update that table when you allocate.

Step 1 — Provision the device (server side)

One command generates the per-device cert, pins its static tunnel IP, and stages the three upload files:

ssh root@147.93.168.216 \
  '/usr/local/sbin/ovpn-add-client.sh <customer-slug> <device-slug> <tunnel-ip>'

# worked example:
# /usr/local/sbin/ovpn-add-client.sh mithra-scan mithra-gxw01 10.21.0.17

It prints the exact ATA settings and the scp line to fetch the files. One device = one cert = one IP. Do not reuse a cert across devices or customers.

Step 2 — Fetch the three files to your Mac

# run FROM your Mac
scp root@147.93.168.216:/etc/openvpn/clients/<customer-slug>/{ca.crt,<device-slug>.crt,<device-slug>.key} ~/Downloads/

# worked example:
scp root@147.93.168.216:/etc/openvpn/clients/mithra-scan/{ca.crt,mithra-gxw01.crt,mithra-gxw01.key} ~/Downloads/

Step 3 — Enter settings in the GXW web UI

Maintenance → VPN → OpenVPN Settings (#page:maintenance_openvpn)

Field Value
OpenVPN Enable Yes
OpenVPN Server Address openvpn.astradial.com (DNS-only A → 147.93.168.216; use the hostname so future prod moves need no device change)
OpenVPN Port 1194
Interface type TUN
OpenVPN Transport UDP
Enable LZO Compression No ⚠️ (server runs no compression — must match)
OpenVPN Encryption AES-256-CBC
OpenVPN Digest SHA256 (the bare one, not RSA-SHA256)
OpenVPN CA Upload ca.crt
OpenVPN Certificate Upload <device-slug>.crt
OpenVPN Client Key Upload <device-slug>.key
OpenVPN Client Key Password (blank) — key is nopass

⚠️ The handover brief said LZO = Yes; we run no compression, so set LZO = No. This is the one place the live config differs from that brief.

HT8xx (HandyTone) ATAs — same server, different UI

The HT8xx HandyTone series (HT814, HT813, etc.) speaks the same OpenVPN and uses the same per-device cert flow, but the web UI differs from the GXW:

GXW42xx HT8xx (e.g. HT814)
OpenVPN settings location Maintenance → VPN → OpenVPN Settings Advanced Settings → OpenVPN® Settings (no "VPN" menu)
TUN/TAP selector TUN/TAP dropdown (pick TUN) None — always TUN
Cipher field "OpenVPN Encryption" + "OpenVPN Digest" "OpenVPN Cipher Method" (set AES-256-CBC); a separate Digest field may not exist on some firmware — that's fine
Auth cert only cert only — leave OpenVPN Username/Password blank
ICMP over tunnel replies to ping does not reply to ping — verify with HTTP (./gxw-tunnel.sh status), not ping

Everything else (server 147.93.168.216:1194 UDP, AES-256-CBC, SHA256, Comp-LZO No, the three uploaded files) is identical. Worked example: Paavai Hospital (paavai-ht81410.21.0.33), provisioned 2026-06-17.

Dinstar DAG (FXS) gateways — single .ovpn, HTTPS web UI

Dinstar DAG-series analog gateways speak the same OpenVPN to the same server, but differ from Grandstream in two ways that matter operationally:

Grandstream GXW / HT8xx Dinstar DAG
OpenVPN config input Three separate uploads (ca.crt + .crt + .key) Single inline .ovpn upload (ca/cert/key embedded). ovpn-add-client.sh still stages the three files — bundle them into one <device>.ovpn (inline <ca>/<cert>/<key>) for the Dinstar. Fall back to fields + 3 files only if a firmware build has no .ovpn import.
Web UI (admin) HTTP on :80 HTTPS on :443 (port 80 is closed). The shared gxw-tunnel.sh assumes HTTP/80, so use the per-customer .command shortcut with DEVICE_PORT=443 and an https:// URL instead.
ICMP over tunnel GXW replies; HT8xx does not replies to ping

Crypto is identical (UDP 1194, AES-256-CBC, SHA256, no compression, no tls-auth). Worked example: DCNet (dcnet-dag0110.21.0.81), provisioned & live 2026-06-30. The .ovpn was built by inlining the three staged files and mirroring astradial-ata.conf's directives (cipher AES-256-CBC, auth SHA256, remote-cert-tls server, no comp-lzo).

The web UI cert is self-signed — the browser warns; click Advanced → Proceed. HTTP 000 on the status check almost always means you probed :80 on a device whose UI is on :443.

⚠️ This is not Dinstar-only. Some HT8xx firmware also serves the admin UI over HTTPS/:443 with HTTP/:80 closed (seen on Thangavelu thangavelu-hospitals-ata01, 2026-07-04). When the Details/status check shows ⛔ HTTP 000 — not reachable but ping/VPN are fine, the device is up — the checker probed the wrong port/scheme. Per-customer .command fix: set DEVICE_PORT=443, use an https:// URL, curl -k, and accept 302 as reachable (the login redirect) — not just 200/401. The provision-client.sh generator now emits this (302 accepted); older generated folders need the manual edit.

Save and ApplyReboot.

Then point the device's SIP Profile at the PBX over the tunnel:

Field Value Why
SIP Server 10.21.0.1:5080 The tunnel gateway. Use 5080, not 5060 — every generated endpoint pins transport=transport-udp, which is bound to 0.0.0.0:5080. Registering to 5060 lands on transport-udp-legacy while Asterisk still sends OPTIONS/INVITE out of 5080 — asymmetric, and the qualify path gets flaky.
Transport UDP
NAT Traversal VPN Profiles → Profile N → Network Settings. Not Keep-Alive — that's the public-path pattern (see the HT813 FXO doc) and leaving it set is what silently keeps a tunnel-connected device on the public internet.
Preferred Vocoder 1 / 2 PCMA / PCMU Audio Settings → Vocoder Settings. Indian carriers are A-law; the Tata trunk is allow=alaw only.
SLIC Setting USA 1 (BELLCORE 600 ohms) (the factory default — leave it) Audio Settings → Analog Line Settings. Match the handset, not the country. These FXS ports drive handsets over short in-building wiring — the gateway is the exchange for those phones; they never touch the Indian PSTN loop, so the "USA/India" label is a red herring. On ACR's handsets USA 1 (600 Ω) is echo-free; INDIA/NEW ZEALAND gave more volume but audible echo (2026-07-18). If the line is too quiet, fix it with RX gain (below), not by switching to a complex-impedance SLIC.
FXS-port SIP user/secret from the editor see credential retrieval below

A tunnel that is Up does not mean SIP is using it

The device's VPN can be connected and healthy while every FXS port still registers over the public internet — the tunnel just carries management traffic and nothing tells you. This is not hypothetical: ACR ran this way for weeks (2026-06-30 → 07-17, ~45 unreachable episodes) with a perfectly healthy 10.21.0.65 tunnel. Verify from the server, never the device UI:

ssh root@147.93.168.216 'asterisk -rx "pjsip show contacts" | grep <ext-prefix>'

The contact source must be the 10.21.0.x tunnel IP. If you see a public IP with x-ast-orig-host=<lan-ip>, the hospital NAT is in the path and the SIP Server / NAT Traversal fields above are wrong. See L-1.

Step 4 — Verify the tunnel & registration

# Server side — watch the device connect:
ssh root@147.93.168.216 'journalctl -u openvpn-server@astradial-ata -f'
#   look for: "<device-slug>" ... "Peer Connection Initiated"

# Connected clients + their tunnel IPs:
ssh root@147.93.168.216 'cat /var/log/openvpn/astradial-ata-status.log'
#   the device should hold its static IP (e.g. 10.21.0.17)

# SIP registration landed:
ssh root@147.93.168.216 'asterisk -rx "pjsip show contacts" | grep <ext-or-ip>'

On the GXW Status page: VPN shows connected with tunnel IP 10.21.0.x; FXS ports show Registered.

Management-access-only devices (e.g. Mithra Scan) won't have SIP registrations — skip the pjsip show contacts check. For those, "working" = the device's tunnel IP pings and its web UI answers (next section).

Step 5 — Reach the device's web UI over the tunnel

The device's admin UI is only on its LAN side; reach it from your Mac by forwarding a local port through the server to the device's tunnel IP:

ssh -L 8080:10.21.0.17:80 root@147.93.168.216     # leave running
# then open  http://localhost:8080  in the browser

Use http:// explicitly and prefer an Incognito window — Chrome otherwise auto-upgrades localhost to HTTPS and the GXW (plain HTTP) returns ERR_SSL_PROTOCOL_ERROR.

Instead of the raw ssh -L, use gxw-tunnel.sh (kept with the per-customer cert folder on the operator's Mac, e.g. ~/AstradialDevelopment/OpenVPN/). It opens the forward, launches Chrome in incognito, and reports status:

./gxw-tunnel.sh start  <device-ip> [local-port]   # open tunnel + incognito Chrome
./gxw-tunnel.sh status <device-ip>                # connected? reachable? web UI? 
./gxw-tunnel.sh stop   <device-ip> [local-port]   # close tunnel

# Mithra Scan example:
./gxw-tunnel.sh start  10.21.0.17

Pass a distinct local-port (e.g. 8081) to manage two devices at once. Per-customer double-clickable .command shortcuts (Start / Stop / Details) wrap the same logic for non-CLI use; copy a customer folder and change DEVICE_IP at the top for a new device.

These helper scripts live alongside the device's private key in a git-ignored folder — never commit that folder (key material).

Retrieving configs & credentials

🔒 No secret values are written in this wiki. This section says where each lives and how to fetch it. Never paste a key, secret, or password into these docs — store them in the team password manager under the customer's entry (e.g. Mithra Scan — GXW4216V2).

Item Where it lives How to retrieve
VPN files (ca.crt, <device>.crt, <device>.key) Server: /etc/openvpn/clients/<customer>/ (root 0600) scp line in Step 2. Re-stageable any time by re-running ovpn-add-client.sh.
OpenVPN client key password None by design (nopass). The GXW field stays blank.
GXW web-admin password 🔒 On the unit's label (random per-unit on current firmware), or set during bench prep Read off the device label, or from the password-manager entry. Store it there at install time.
SIP line credentials (FXS user + secret) 🔒 The editor (editor.astradial.com) under the customer's org — endpoints are editor/AstraPBX-managed (hashed e<id>/<ext> names), not hand-written Editor UI → org → endpoint. On-server lookup (prints the secret — do not paste into docs): ssh root@147.93.168.216 'asterisk -rx "pjsip show auth <auth-id>"'
SSH to the cloud host 🔒 Team password manager / your SSH key ssh root@147.93.168.216

Decommission / replace a device

ssh root@147.93.168.216
cd /etc/openvpn/easy-rsa
./easyrsa revoke <device-slug>      # invalidate the one device's cert
./easyrsa gen-crl                    # regenerate the revocation list
rm -f /etc/openvpn/ccd/<device-slug> # free its static IP
rm -rf /etc/openvpn/clients/<customer>/   # if the whole customer is leaving

If you start using the CRL for live enforcement, add crl-verify /etc/openvpn/easy-rsa/pki/crl.pem to astradial-ata.conf and restart the service. Not enabled yet (no revocations to date).

Troubleshooting

Symptom Likely cause Fix
Stuck "connecting" / TLS handshake fails The GXW firmware is old; OpenSSL 3 on Debian 13 may refuse its TLS version On server, add tls-version-min 1.1 (and providers legacy default if needed) to astradial-ata.conf; restart
Connects + gets its IP, then drops after a few minutes; server log shows Inactivity timeout (--ping-restart) then SIGUSR1[soft,ping-restart] received Device stopped sending keepalives — almost always site-side: unit powered off / rebooted, hospital internet dropped, or NAT expiring the idle UDP pinhole. The crypto + IP push succeeding before the drop means the config is correct — this is connectivity, not a cert/cipher mismatch. Confirm the unit is powered and the site has internet (check its public IP), then reboot it or toggle OpenVPN Enable off→on. It re-registers in ~30 s. Verify with ./gxw-tunnel.sh status <ip> or cat /var/log/openvpn/astradial-ata-status.log. Worked example: Paavai HT814 connected 2026-06-17 17:20, then ping-restarted.
No response at all UDP 1194 not reaching the box Confirm iptables -L INPUT 1 shows the dpt:1194 ACCEPT at the top; re-run openvpn-customer-iptables.sh up
Bad LZO decompression header Compression mismatch Ensure GXW LZO = No (server runs none)
TLS handshake OK, no SIP register SIP Profile not pointed at 10.21.0.1, or wrong FXS creds Set SIP server 10.21.0.1:5080 (not 5060 — see the SIP table above); recheck creds from the editor
Cert verify fail Wrong ca.crt or cert not signed by our CA Re-fetch all three files from /etc/openvpn/clients/<customer>/
Line too quiet (staff can't hear the caller) RX gain attenuating; factory default is -6 dB Raise RX Gain — see Audio tuning below. Don't switch SLIC for volume.
Idle phone returns busy to callers ("fake busy") FXS port stuck from a lost BYE over NAT See Error 101. Root fix = the tunnel repoint above.

Audio tuning — volume, echo, and the Save-and-Apply blip

Two independent knobs, don't swap them (Audio Settings → Analog Line Settings):

  • RX Gain = level the ATA feeds the handset earpiece"staff can't hear the caller." Default -6 dB (attenuating).
  • TX Gain = level from the handset mic toward the network → "the caller can't hear staff." Default -4 dB.
  • SLIC = impedance/hybrid balance → sets echo (and shifts loudness as a side effect). Use it for echo, not for volume.

To raise volume (worked example: ACR, 2026-07-18, raised in steps to +6 dB):

  1. Raise RX Gain in steps (-6 → 0 → +3 → +6; +6 is the ceiling). Save and Apply between steps.
  2. After each raise, test the FAR end for echo/distortion — call in from a mobile and confirm the caller doesn't hear their own voice echoing and that loud speech isn't crackly. ACR staff won't notice echo; only the far party will, because higher RX leaks back through the hybrid into TX (USA 1 impedance + the enabled Line Echo Canceller mitigate, but +6 pushes hard). If echo/distortion appears, drop back one step.
  3. +6 is the last stop the ATA can offer. Still too quiet? Use the handset's own earpiece volume or an amplified/HAC handset — not more ATA gain.

Save and Apply briefly drops all ports (~5 s)

Applying any profile change re-registers every FXS port — a few seconds of dead air. This once triggered a false "hospital outage" alarm at ACR. It is not an outage and not a reason to reboot; it self-recovers in seconds. Make audio changes in a quiet moment. If calls are still dead after ~1 min, diagnose from the server (pjsip show contacts) — the device's "Registered" status is device-initiated and always looks green even when the server can't reach it (see L-1).

See also