Fix keep_alive_interval placement: [global], not [transport-tls]

keep_alive_interval is a type=global pjsip.conf option, not a
type=transport option -- it never existed on [transport-tls] on any
Asterisk version. The IONOS TLS-keepalive mitigation was inserting it
there, which made sorcery reject the whole transport-tls object
("Could not find option suitable for category 'transport-tls' named
'keep_alive_interval'"), silently killing TLS SIP entirely instead of
just adding a keepalive.

Both _asterisk_patch_keepalive_vendor_files (deployed vendor copies)
and _asterisk_ensure_live_keepalive (live pjsip.conf) now target
[global]/type=global, and both self-heal a box that already picked up
the bad placement by removing it from [transport-tls] first.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SpKTLpwAgZNooTacWeQLuc
This commit is contained in:
Claude
2026-08-22 03:10:37 +00:00
parent 4eda82acce
commit 47c04ebeb3
+55 -19
View File
@@ -1284,7 +1284,7 @@ _asterisk_ensure_live_voicemail_include() {
docker exec "$CONTAINER_NAME" asterisk -rx "dialplan reload" &>/dev/null || true
}
# ── pjsip.conf: [transport-tls] keep_alive_interval ─────────────────────────
# ── pjsip.conf: [global] keep_alive_interval ─────────────────────────────────
# Mitigation for WiFi/LAN SIP clients (e.g. Sipnetic on Android) dropping
# their TLS registration every few seconds on some hosts but not others —
# confirmed live on an IONOS VPS (never reproduced on a DigitalOcean droplet
@@ -1293,15 +1293,28 @@ _asterisk_ensure_live_voicemail_include() {
# live disconnect and CrowdSec/packet-loss both ruled out first. Leading
# theory: an idle-connection timeout somewhere in IONOS's own network
# virtualization layer, below anything client-side tools can see. PJSIP's
# `keep_alive_interval` sends a lightweight double-CRLF over the transport's
# TLS connection on a timer, which is the standard fix for exactly this
# `keep_alive_interval` sends a lightweight double-CRLF over connection-oriented
# transports (TCP/TLS) on a timer, which is the standard fix for exactly this
# class of "idle SIP/TLS connection gets silently dropped" symptom.
#
# This is a *transport* option, not an endpoint/AOR option — `qualify_frequency`
# (already set globally) is an endpoint-level OPTIONS ping that re-establishes
# a dropped connection, it doesn't stop the drop from happening in the first
# place. `rtp_keepalive` (mobile devices) is unrelated: RTP media keepalive
# during an active call, not SIP signaling connection keepalive while idle.
# **This is a `type=global` option, not a `type=transport` option**it does
# not exist on `[transport-tls]`/`[transport-tcp]` objects at all, on any
# Asterisk version. An earlier version of this patch inserted it into
# `[transport-tls]` (right after `protocol=tls`), which sorcery always
# rejects: "Could not find option suitable for category 'transport-tls'
# named 'keep_alive_interval'" — and rejecting the option means the whole
# `type=transport` object fails to be created, so `transport-tls` never
# binds at all. Confirmed live: this silently took down TLS SIP entirely on
# a box that had picked up the patch, reproducing the exact same "network
# error on all calls" symptom the rest of this file's health check exists to
# catch. The anchor below targets `type=global` inside `[global]` instead.
#
# This is a *transport-behavior* option, not an endpoint/AOR option —
# `qualify_frequency` (already set globally) is an endpoint-level OPTIONS
# ping that re-establishes a dropped connection, it doesn't stop the drop
# from happening in the first place. `rtp_keepalive` (mobile devices) is
# unrelated: RTP media keepalive during an active call, not SIP signaling
# connection keepalive while idle.
_asterisk_patch_keepalive_vendor_files() {
local EA_DIR="$1"
local ENTRYPOINT="$EA_DIR/docker/entrypoint.sh"
@@ -1313,15 +1326,20 @@ _asterisk_patch_keepalive_vendor_files() {
for f in "$ENTRYPOINT" "$EASY1" "$EASY2"; do
[[ -f "$f" ]] || continue
if ! grep -q '^protocol=tls$' "$f"; then
log_warning "$(basename "$f"): '[transport-tls]' anchor not found — vendor template changed upstream."
log_warning " Add 'keep_alive_interval=15' manually inside [transport-tls] in this file's pjsip.conf heredoc."
# Undo the old, incorrect [transport-tls] placement if an earlier
# run of this function already patched it there.
if grep -q '^protocol=tls$' "$f" && grep -A2 '^protocol=tls$' "$f" | grep -q '^keep_alive_interval='; then
sed -i '/^protocol=tls$/{n;/^keep_alive_interval=/d}' "$f"
fi
if ! grep -q '^type=global$' "$f"; then
log_warning "$(basename "$f"): '[global]' anchor not found — vendor template changed upstream."
log_warning " Add 'keep_alive_interval=15' manually inside [global] (not [transport-tls]) in this file's pjsip.conf heredoc."
continue
fi
grep -q '^keep_alive_interval=' "$f" || sed -i '/^protocol=tls$/a keep_alive_interval=15' "$f"
grep -q '^keep_alive_interval=' "$f" || sed -i '/^type=global$/a keep_alive_interval=15' "$f"
done
log_success "Vendor generator functions patched for TLS transport keepalive."
log_success "Vendor generator functions patched for SIP keepalive (pjsip.conf [global])."
}
# Live-file counterpart to the vendor-template patch above, same reasoning
@@ -1332,20 +1350,38 @@ _asterisk_patch_keepalive_vendor_files() {
# a transport object — PJSIP transports are bound at module load, not
# reloadable via sorcery like endpoints/AORs are — so this restarts the
# container rather than issuing a reload, same as _asterisk_ensure_live_dangerously.
#
# `keep_alive_interval` belongs in `[global]` (`type=global`), not
# `[transport-tls]` — see the comment on _asterisk_patch_keepalive_vendor_files
# for why the old placement made sorcery reject the transport object outright
# (killing TLS SIP entirely, not just the keepalive). This also self-heals a
# box that already has the bad `[transport-tls]` entry from before that fix.
_asterisk_ensure_live_keepalive() {
local EA_DIR="$1" CONTAINER_NAME="$2"
local CONF_LIVE="$EA_DIR/config/asterisk/pjsip.conf"
[[ -f "$CONF_LIVE" ]] || return 0
if ! grep -q '^\[transport-tls\]$' "$CONF_LIVE"; then
log_warning "Couldn't find '[transport-tls]' in the live pjsip.conf — add"
local CHANGED=false
if sed -n '/^\[transport-tls\]$/,/^\[/{/^keep_alive_interval=/p}' "$CONF_LIVE" | grep -q .; then
sed -i '/^\[transport-tls\]$/,/^\[/{/^keep_alive_interval=/d}' "$CONF_LIVE"
log_warning "Removed 'keep_alive_interval' from [transport-tls] — that option doesn't exist on a PJSIP"
log_warning "transport object and was making the whole TLS transport fail to bind. Moving it to [global]."
CHANGED=true
fi
if ! grep -q '^\[global\]$' "$CONF_LIVE"; then
log_warning "Couldn't find '[global]' in the live pjsip.conf — add"
log_warning "'keep_alive_interval=15' manually inside that section, then: docker restart ${CONTAINER_NAME}"
return 0
fi
grep -q '^keep_alive_interval=' "$CONF_LIVE" && return 0
sed -i '/^\[transport-tls\]$/,/^\[/{/^protocol=tls$/a keep_alive_interval=15
if ! grep -q '^keep_alive_interval=' "$CONF_LIVE"; then
sed -i '/^\[global\]$/,/^\[/{/^type=global$/a keep_alive_interval=15
}' "$CONF_LIVE"
log_success "Patched keep_alive_interval=15 into the live pjsip.conf's [transport-tls] transport."
log_success "Patched keep_alive_interval=15 into the live pjsip.conf's [global] section."
CHANGED=true
fi
[[ "$CHANGED" == true ]] || return 0
log_info "Restarting Asterisk to apply (transport options aren't picked up by a reload)..."
docker restart "$CONTAINER_NAME" &>/dev/null \