From 7ffa5f6e6f07b9995deab6418aae4f73b4d30ee0 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Feb 2026 18:06:34 +0000 Subject: [PATCH] Fix TLS cert (add SANs) and enhance logging for registration debugging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phones were not registering with zero log entries — two root causes: 1. Self-signed cert lacked Subject Alternative Names (SANs). Modern TLS clients (iOS/Android SIP apps) require SANs and ignore CN-only certs, causing silent TLS handshake rejection. Cert generation now includes SANs (DNS + IP), and existing certs without SANs are auto-regenerated. 2. Logger only captured notice/warning/error — TLS handshake failures are logged at the security level. Added security to console logging. Also added: - Startup check verifying port 5061 is actually bound - TLS/cert diagnostics in vpn-diagnostics (SAN check, port check, self-signed warning with guidance for phone configuration) https://claude.ai/code/session_01Vm6NLaQuzM4VosAotqS1q8 --- docker/entrypoint.sh | 51 +++++++++++++++++++++++++++----- scripts/vpn-diagnostics.sh | 60 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 8 deletions(-) diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 4e86afc..ed7cf73 100644 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -75,16 +75,27 @@ if [[ "$raw_cidr" =~ \.([0-9]+)/([0-9]+)$ ]]; then default_cidr="${raw_cidr%.*}.0/${BASH_REMATCH[2]}" fi -# ── 5. Generate self-signed certs if missing ────────────────── +# ── 5. Generate self-signed certs ────────────────────────────── +# Regenerate if missing OR if existing cert lacks SANs (modern TLS clients require them) +regen_cert=false if [[ ! -f /etc/asterisk/certs/server.crt ]]; then + regen_cert=true +elif ! openssl x509 -in /etc/asterisk/certs/server.crt -noout -ext subjectAltName 2>/dev/null | grep -q "DNS:"; then + log_info "Existing TLS cert lacks SANs — regenerating for mobile phone compatibility" + regen_cert=true +fi + +if $regen_cert; then log_info "Generating self-signed TLS certificate..." mkdir -p /etc/asterisk/certs - # Use DOMAIN_NAME as CN if available cn="${DOMAIN_NAME:-asterisk-local}" + # Include Subject Alternative Names — required by modern TLS clients (iOS/Android SIP apps) openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \ -keyout /etc/asterisk/certs/server.key \ -out /etc/asterisk/certs/server.crt \ - -subj "/CN=${cn}" 2>/dev/null + -subj "/CN=${cn}" \ + -addext "subjectAltName=DNS:${cn}${PUBLIC_IP:+,IP:${PUBLIC_IP}}" \ + 2>/dev/null chown asterisk:asterisk /etc/asterisk/certs/server.* chmod 644 /etc/asterisk/certs/server.crt chmod 600 /etc/asterisk/certs/server.key @@ -287,13 +298,13 @@ rungroup = asterisk EOF fi -if [[ ! -f /etc/asterisk/logger.conf ]]; then - cat > /etc/asterisk/logger.conf << 'EOF' +# ── logger.conf (always regenerated - ensures security logging is on) ── +cat > /etc/asterisk/logger.conf << 'EOF' [general] [logfiles] -console => notice,warning,error +; security level captures TLS handshake failures and auth issues +console => notice,warning,error,security EOF -fi # ── modules.conf (always regenerated - ensures chan_sip stays disabled) ── cat > /etc/asterisk/modules.conf << 'EOF' @@ -368,6 +379,24 @@ for i in $(seq 1 60); do sleep 1 done +# Verify PJSIP transports are listening +tls_ok=false +udp_ok=false +if asterisk -rx "pjsip show transports" 2>/dev/null | grep -q "transport-tls"; then + tls_ok=true +fi +if asterisk -rx "pjsip show transports" 2>/dev/null | grep -q "transport-udp"; then + udp_ok=true +fi + +# Check if port 5061 is actually bound +tls_listen="" +if command -v ss &>/dev/null; then + tls_listen=$(ss -tlnp 2>/dev/null | grep ":5061 " || true) +elif command -v netstat &>/dev/null; then + tls_listen=$(netstat -tlnp 2>/dev/null | grep ":5061 " || true) +fi + echo "" echo -e "${CYAN}══════════════════════════════════════════════════════════════${NC}" echo -e "${CYAN} Easy Asterisk (Docker)${NC}" @@ -375,7 +404,13 @@ echo -e "${CYAN}═════════════════════ echo -e " FQDN: ${GREEN}${DOMAIN_NAME:-not set}${NC}" echo -e " Public IP: ${GREEN}${PUBLIC_IP:-unknown}${NC}" echo -e " TURN/STUN: ${GREEN}${turn_server}${NC}" -echo -e " TLS: ${GREEN}Enabled (port 5061)${NC}" +if $tls_ok && [[ -n "$tls_listen" ]]; then + echo -e " TLS: ${GREEN}Enabled (port 5061)${NC}" +elif $tls_ok; then + echo -e " TLS: ${YELLOW}Transport loaded but port 5061 not bound — check certs${NC}" +else + echo -e " TLS: ${RED}NOT LOADED — check Asterisk logs${NC}" +fi echo -e " ICE: ${GREEN}Enabled${NC}" echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}" echo -e " SIP clients connect to: ${GREEN}${DOMAIN_NAME:-$local_ip}:5061${NC} (TLS)" diff --git a/scripts/vpn-diagnostics.sh b/scripts/vpn-diagnostics.sh index b928f82..550f7b5 100644 --- a/scripts/vpn-diagnostics.sh +++ b/scripts/vpn-diagnostics.sh @@ -127,6 +127,66 @@ else fail "pjsip.conf not found" fi +# ── Test 2b: TLS Certificate & Port Checks ──────────────────── +echo "" +echo -e "${BOLD}2b. TLS / Certificate Status${NC}" +echo "" + +# Check if port 5061 is actually listening +if command -v ss &>/dev/null; then + tls_listen=$(ss -tlnp 2>/dev/null | grep ":5061 " || true) +elif command -v netstat &>/dev/null; then + tls_listen=$(netstat -tlnp 2>/dev/null | grep ":5061 " || true) +else + tls_listen="" +fi + +if [[ -n "$tls_listen" ]]; then + pass "Port 5061 (TLS) is listening" +else + fail "Port 5061 (TLS) is NOT listening" + warn "Asterisk TLS transport failed to start — check certs and logs" +fi + +# Check TLS cert +cert_file="/etc/asterisk/certs/server.crt" +if [[ -f "$cert_file" ]]; then + pass "TLS certificate exists: $cert_file" + + # Check cert CN/SAN + cert_cn=$(openssl x509 -in "$cert_file" -noout -subject 2>/dev/null | sed 's/.*CN *= *//') + cert_san=$(openssl x509 -in "$cert_file" -noout -ext subjectAltName 2>/dev/null | grep -oP 'DNS:\K[^,]+' || true) + cert_expiry=$(openssl x509 -in "$cert_file" -noout -enddate 2>/dev/null | cut -d= -f2) + + info "Cert CN: ${cert_cn:-unknown}" + if [[ -n "$cert_san" ]]; then + pass "Cert has SAN (Subject Alt Name): ${cert_san}" + else + fail "Cert has NO SAN — modern phones (iOS/Android) will reject it" + warn "Delete /etc/asterisk/certs/server.crt and restart to regenerate with SANs" + fi + info "Cert expires: ${cert_expiry:-unknown}" + + # Check if cert is self-signed + issuer=$(openssl x509 -in "$cert_file" -noout -issuer 2>/dev/null | sed 's/.*CN *= *//') + if [[ "$issuer" == "$cert_cn" ]]; then + warn "Cert is SELF-SIGNED — phones must be set to accept self-signed certs" + info "In your SIP app: disable TLS certificate verification / allow self-signed" + fi + + # Verify PJSIP transport loaded it + if command -v asterisk &>/dev/null; then + transport_status=$(asterisk -rx "pjsip show transports" 2>/dev/null || true) + if echo "$transport_status" | grep -q "transport-tls"; then + pass "PJSIP TLS transport is loaded" + else + fail "PJSIP TLS transport NOT loaded — cert may be invalid" + fi + fi +else + fail "TLS certificate not found at $cert_file" +fi + # ── Test 3: Check RTP and ICE/STUN configuration ───────────── echo "" echo -e "${BOLD}3. RTP / ICE / STUN Configuration${NC}"