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-gxw01→10.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-ata→active. - A free
/28and 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-ht814 → 10.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-dag01 → 10.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 000on the status check almost always means you probed:80on 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 theDetails/status check shows⛔ HTTP 000 — not reachablebut ping/VPN are fine, the device is up — the checker probed the wrong port/scheme. Per-customer.commandfix: setDEVICE_PORT=443, use anhttps://URL,curl -k, and accept302as reachable (the login redirect) — not just200/401. Theprovision-client.shgenerator now emits this (302 accepted); older generated folders need the manual edit.
Save and Apply → Reboot.
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:
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 contactscheck. 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.
Helper tool (recommended)¶
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.pemtoastradial-ata.confand 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):
- Raise RX Gain in steps (
-6 → 0 → +3 → +6;+6is the ceiling). Save and Apply between steps. - 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
+6pushes hard). If echo/distortion appears, drop back one step. +6is 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¶
- OpenVPN ATA Tunnels — architecture & security model
- Connect a Customer Router to the VPN (WireGuard) — the sibling flow for site routers
- Troubleshooting → Error 95: Alphion ONT FXS gateway — fake busy (SIP ALG), can't-dial-out (P/T switch + stuck channel), enable call waiting