diff --git a/.env.example b/.env.example index 6b1cfa5..0c5ef1c 100644 --- a/.env.example +++ b/.env.example @@ -77,19 +77,9 @@ TELEPHONY_DEV_WS_PORT=8088 TELEPHONY_DEV_AMI_PORT=5038 TELEPHONY_DEV_SIP_PORT=5060 TELEPHONY_DEV_RTP_MIN_PORT=10000 -TELEPHONY_DEV_RTP_MAX_PORT=10020 - -# Optionales FreePBX-Diagnoseprofil. Das Profil ist nur als Referenz-PBX für -# Provider-Tests gedacht und nutzt eigene Ports, damit der FEDEO-Asterisk -# parallel bestehen bleibt. -FREEPBX_DEV_IMAGE=escomputers/freepbx:17-nofail2ban -FREEPBX_DEV_PLATFORM=linux/amd64 -FREEPBX_DEV_TZ=Europe/Berlin -FREEPBX_DEV_HTTP_PORT=18080 -FREEPBX_DEV_HTTPS_PORT=18443 -FREEPBX_DEV_SIP_PORT=15060 -FREEPBX_DEV_RTP_MIN_PORT=18000 -FREEPBX_DEV_RTP_MAX_PORT=18100 +TELEPHONY_DEV_RTP_MAX_PORT=10100 +TELEPHONY_ASTERISK_EXTERNAL_SIGNALING_ADDRESS= +TELEPHONY_ASTERISK_EXTERNAL_MEDIA_ADDRESS= # Externe Telefonie über Telekom/tel.t-online.de. Keine echten Zugangsdaten # einchecken. SIP-ID ist in der Regel die Rufnummer mit Vorwahl ohne Leerzeichen diff --git a/backend/src/routes/telephony.ts b/backend/src/routes/telephony.ts index 0adbe73..330df83 100644 --- a/backend/src/routes/telephony.ts +++ b/backend/src/routes/telephony.ts @@ -265,6 +265,7 @@ const renderProviderExtensionsConfig = (trunk: any) => { const outboundPrefix = asteriskValue(trunk.outboundPrefix) || "0" const escapedPrefix = outboundPrefix.replace(/[^0-9*#+]/g, "") const callerId = asteriskValue(trunk.callerId) || asteriskValue(trunk.sipUser) + const inboundDid = callerId.replace(/[^0-9+]/g, "") return [ "; Von FEDEO generiert. Änderungen im Container können überschrieben werden.", @@ -282,6 +283,12 @@ const renderProviderExtensionsConfig = (trunk: any) => { " same => n,Hangup()", "", `[from-${providerKey}]`, + ...(inboundDid ? [ + `exten => ${inboundDid},1,NoOp(FEDEO eingehend über ${provider.label} DID: $` + "{EXTEN})", + ` same => n,Dial(PJSIP/${inboundExtension},30)`, + " same => n,Hangup()", + "", + ] : []), `exten => s,1,NoOp(FEDEO eingehend über ${provider.label})`, ` same => n,Dial(PJSIP/${inboundExtension},30)`, " same => n,Hangup()", @@ -293,6 +300,25 @@ const renderProviderExtensionsConfig = (trunk: any) => { ].join("\n") } +const renderWebRtcConfig = (trunk: any) => { + const externalMediaAddress = asteriskValue(trunk?.externalMediaAddress || trunk?.externalSignalingAddress) + + if (!externalMediaAddress) { + return [ + "; Von FEDEO generiert.", + "; Kein externes WebRTC-Media-Rewrite konfiguriert.", + "", + ].join("\n") + } + + return [ + "; Von FEDEO generiert. Diese Datei wird innerhalb des FEDEO-WebRTC-Templates inkludiert.", + `media_address=${externalMediaAddress}`, + "bind_rtp_to_media_address=yes", + "", + ].join("\n") +} + const renderTelekomTransportConfig = (trunk: any) => { const externalSignalingAddress = asteriskValue(trunk?.externalSignalingAddress) const externalMediaAddress = asteriskValue(trunk?.externalMediaAddress || trunk?.externalSignalingAddress) @@ -339,6 +365,10 @@ const writeAsteriskTrunkConfig = async (trunk: any) => { name: "pjsip.transport.conf", content: renderTelekomTransportConfig(trunk), }, + { + name: "pjsip.webrtc.conf", + content: renderWebRtcConfig(trunk), + }, ] await Promise.all(files.map(async (file) => { diff --git a/docker-compose.yml b/docker-compose.yml index 984b578..a5d3703 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -125,6 +125,8 @@ services: - TELEPHONY_TELEKOM_CALLER_ID=${TELEPHONY_TELEKOM_CALLER_ID:-} - TELEPHONY_TELEKOM_INBOUND_EXTENSION=${TELEPHONY_TELEKOM_INBOUND_EXTENSION:-1001} - TELEPHONY_TELEKOM_OUTBOUND_PREFIX=${TELEPHONY_TELEKOM_OUTBOUND_PREFIX:-0} + - TELEPHONY_ASTERISK_EXTERNAL_SIGNALING_ADDRESS=${TELEPHONY_ASTERISK_EXTERNAL_SIGNALING_ADDRESS:-} + - TELEPHONY_ASTERISK_EXTERNAL_MEDIA_ADDRESS=${TELEPHONY_ASTERISK_EXTERNAL_MEDIA_ADDRESS:-} - ASTERISK_GENERATED_DIR=/etc/asterisk/generated command: - /bin/sh @@ -138,50 +140,7 @@ services: - "${TELEPHONY_DEV_WS_PORT:-8088}:8088" - "${TELEPHONY_DEV_AMI_PORT:-5038}:5038" - "${TELEPHONY_DEV_SIP_PORT:-5060}:5060/udp" - - "${TELEPHONY_DEV_RTP_MIN_PORT:-10000}-${TELEPHONY_DEV_RTP_MAX_PORT:-10020}:10000-10020/udp" - networks: - - traefik - - freepbx-dev-db: - image: mariadb:10.11 - restart: unless-stopped - profiles: - - freepbx-dev - environment: - MYSQL_ROOT_PASSWORD_FILE: /run/secrets/freepbx_mysql_root_password - MYSQL_USER: freepbxuser - MYSQL_PASSWORD_FILE: /run/secrets/freepbx_user_password - secrets: - - freepbx_mysql_root_password - - freepbx_user_password - volumes: - - freepbx_dev_mysql:/var/lib/mysql - - ./telephony/freepbx/my.cnf:/etc/mysql/my.cnf:ro - - ./telephony/freepbx/init.sql:/docker-entrypoint-initdb.d/init.sql:ro - networks: - - traefik - - freepbx-dev: - image: ${FREEPBX_DEV_IMAGE:-escomputers/freepbx:17-nofail2ban} - platform: ${FREEPBX_DEV_PLATFORM:-linux/amd64} - restart: unless-stopped - profiles: - - freepbx-dev - depends_on: - - freepbx-dev-db - environment: - - TZ=${FREEPBX_DEV_TZ:-Europe/Berlin} - secrets: - - freepbx_user_password - - freepbx_postfix_sasl_passwd - volumes: - - freepbx_dev_var:/var - - freepbx_dev_etc:/etc - ports: - - "${FREEPBX_DEV_HTTP_PORT:-18080}:80/tcp" - - "${FREEPBX_DEV_HTTPS_PORT:-18443}:443/tcp" - - "${FREEPBX_DEV_SIP_PORT:-15060}:5060/udp" - - "${FREEPBX_DEV_RTP_MIN_PORT:-18000}-${FREEPBX_DEV_RTP_MAX_PORT:-18100}:18000-18100/udp" + - "${TELEPHONY_DEV_RTP_MIN_PORT:-10000}-${TELEPHONY_DEV_RTP_MAX_PORT:-10100}:10000-10100/udp" networks: - traefik @@ -532,19 +491,6 @@ services: - "./traefik/logs:/logs" networks: - traefik -volumes: - freepbx_dev_mysql: - freepbx_dev_var: - freepbx_dev_etc: - -secrets: - freepbx_mysql_root_password: - file: ./telephony/freepbx/secrets/mysql_root_password.txt - freepbx_user_password: - file: ./telephony/freepbx/secrets/freepbxuser_password.txt - freepbx_postfix_sasl_passwd: - file: ./telephony/freepbx/secrets/sasl_passwd.txt - networks: traefik: external: false diff --git a/docs/telekom-telefonie.md b/docs/telekom-telefonie.md index 66102d8..6152860 100644 --- a/docs/telekom-telefonie.md +++ b/docs/telekom-telefonie.md @@ -66,38 +66,6 @@ Wenn `TELEPHONY_TELEKOM_AUTH_USER` leer bleibt, verwendet Asterisk automatisch ` docker compose --profile telephony-dev up -d asterisk-dev ``` -Beim Start erzeugt der Container die Dateien `pjsip.telekom.conf` und `extensions.telekom.conf` in einem Docker-Volume. Ausgehende Anrufe mit Prefix `0` und internationale Ziele mit `+` werden über den Telekom-Trunk geroutet. Eingehende Anrufe landen standardmäßig auf Nebenstelle `1001`. +Beim Start erzeugt der Container die Dateien `pjsip.telekom.conf`, `extensions.telekom.conf` und `pjsip.webrtc.conf` in einem Docker-Volume. Ausgehende Anrufe mit Prefix `0` und internationale Ziele mit `+` werden über den Trunk geroutet. Eingehende Anrufe landen standardmäßig auf Nebenstelle `1001`; die konfigurierte Rufnummer wird zusätzlich als DID direkt geroutet. -## FreePBX als Diagnose-PBX - -Für Provider-Tests kann zusätzlich das optionale Profil `freepbx-dev` gestartet werden. FreePBX ist hier nicht als dauerhafte FEDEO-Abhängigkeit gedacht, sondern als Referenzoberfläche, um Trunk-, NAT-, CLIP- und Routing-Parameter gegen einen Provider wie Easybell zu prüfen. - -```bash -docker compose --profile freepbx-dev up -d freepbx-dev-db freepbx-dev -``` - -Beim ersten Start muss FreePBX einmal gegen die lokale MariaDB installiert werden: - -```bash -docker compose --profile freepbx-dev exec -T -w /usr/local/src/freepbx freepbx-dev \ - bash -lc 'php install -n --dbuser=freepbxuser --dbpass="$(cat /run/secrets/freepbx_user_password)" --dbhost=freepbx-dev-db' -``` - -Danach ist die Oberfläche lokal unter `http://localhost:18080` erreichbar. Beim ersten Öffnen zeigt FreePBX die Ersteinrichtung für den Web-Admin-Benutzer. Diese Zugangsdaten gelten nur für die FreePBX-Diagnoseoberfläche und sind unabhängig von FEDEO. - -Die Standardports sind bewusst konfliktarm gesetzt: - -- Web: `18080` / `18443` -- SIP UDP: `15060` -- RTP UDP: `18000-18100` - -Das verwendete FreePBX-Image ist aktuell nur für `linux/amd64` veröffentlicht. Auf Apple-Silicon-Hosts nutzt Docker Desktop deshalb über `FREEPBX_DEV_PLATFORM=linux/amd64` Emulation; für Diagnosezwecke ist das ausreichend, aber nicht als Produktionssetup gedacht. - -Für einen möglichst realistischen Easybell-Test kann der FEDEO-Asterisk kurz gestoppt und FreePBX auf dem üblichen SIP-Port gestartet werden: - -```bash -docker compose --profile telephony-dev stop asterisk-dev -FREEPBX_DEV_SIP_PORT=5060 docker compose --profile freepbx-dev up -d freepbx-dev -``` - -In FreePBX sollte der RTP-Bereich unter **Settings -> Asterisk SIP Settings** ebenfalls auf `18000-18100` gesetzt werden, damit er zur Compose-Portfreigabe passt. Wenn der Trunk dort erfolgreich registriert und ein Testanruf möglich ist, können die funktionierenden PJSIP- und NAT-Werte nach FEDEO übernommen werden. +Wenn Asterisk auf einem VPS oder hinter NAT läuft, sollten `TELEPHONY_ASTERISK_EXTERNAL_SIGNALING_ADDRESS` und `TELEPHONY_ASTERISK_EXTERNAL_MEDIA_ADDRESS` auf die öffentliche Asterisk-Adresse gesetzt werden. FEDEO schreibt daraus die WebRTC-Media-Adresse, damit Browser-Clients den RTP/ICE-Pfad sauber aushandeln. diff --git a/telephony/asterisk/pjsip.conf b/telephony/asterisk/pjsip.conf index 87b2298..1f7efe2 100644 --- a/telephony/asterisk/pjsip.conf +++ b/telephony/asterisk/pjsip.conf @@ -31,6 +31,7 @@ force_rport=yes rewrite_contact=yes rtp_symmetric=yes transport=transport-ws +#tryinclude generated/pjsip.webrtc.conf from_domain=localhost [1001](fedeo-webrtc) diff --git a/telephony/asterisk/rtp.conf b/telephony/asterisk/rtp.conf index 1948e0e..dc61371 100644 --- a/telephony/asterisk/rtp.conf +++ b/telephony/asterisk/rtp.conf @@ -1,5 +1,5 @@ [general] rtpstart=10000 -rtpend=10020 +rtpend=10100 icesupport=yes strictrtp=no diff --git a/telephony/freepbx/init.sql b/telephony/freepbx/init.sql deleted file mode 100644 index 4fea153..0000000 --- a/telephony/freepbx/init.sql +++ /dev/null @@ -1,7 +0,0 @@ -CREATE DATABASE IF NOT EXISTS asterisk; -GRANT ALL PRIVILEGES ON `asterisk`.* TO 'freepbxuser'@'%'; - -CREATE DATABASE IF NOT EXISTS asteriskcdrdb; -GRANT ALL PRIVILEGES ON `asteriskcdrdb`.* TO 'freepbxuser'@'%'; - -FLUSH PRIVILEGES; diff --git a/telephony/freepbx/my.cnf b/telephony/freepbx/my.cnf deleted file mode 100644 index abca86c..0000000 --- a/telephony/freepbx/my.cnf +++ /dev/null @@ -1,2 +0,0 @@ -[mysqld] -sql_mode=NO_ENGINE_SUBSTITUTION diff --git a/telephony/freepbx/secrets/freepbxuser_password.txt b/telephony/freepbx/secrets/freepbxuser_password.txt deleted file mode 100644 index adafd94..0000000 --- a/telephony/freepbx/secrets/freepbxuser_password.txt +++ /dev/null @@ -1 +0,0 @@ -change-this-freepbx-user-password diff --git a/telephony/freepbx/secrets/mysql_root_password.txt b/telephony/freepbx/secrets/mysql_root_password.txt deleted file mode 100644 index 350b98f..0000000 --- a/telephony/freepbx/secrets/mysql_root_password.txt +++ /dev/null @@ -1 +0,0 @@ -change-this-freepbx-root-password diff --git a/telephony/freepbx/secrets/sasl_passwd.txt b/telephony/freepbx/secrets/sasl_passwd.txt deleted file mode 100644 index 6edad7e..0000000 --- a/telephony/freepbx/secrets/sasl_passwd.txt +++ /dev/null @@ -1 +0,0 @@ -[smtp.example.com]:587 user@example.com:change-this-app-password diff --git a/telephony/render-asterisk-config.sh b/telephony/render-asterisk-config.sh index ceb108a..affa881 100755 --- a/telephony/render-asterisk-config.sh +++ b/telephony/render-asterisk-config.sh @@ -6,6 +6,7 @@ mkdir -p "$GENERATED_DIR" PJSIP_FILE="$GENERATED_DIR/pjsip.telekom.conf" EXTENSIONS_FILE="$GENERATED_DIR/extensions.telekom.conf" +PJSIP_WEBRTC_FILE="$GENERATED_DIR/pjsip.webrtc.conf" enabled="${TELEPHONY_TELEKOM_ENABLED:-false}" @@ -21,6 +22,9 @@ if [ "$enabled" != "true" ] && [ "$enabled" != "1" ] && [ "$enabled" != "yes" ]; EOF cat > "$EXTENSIONS_FILE" <<'EOF' ; Telekom-Anbindung ist deaktiviert. +EOF + cat > "$PJSIP_WEBRTC_FILE" <<'EOF' +; Kein externes WebRTC-Media-Rewrite konfiguriert. EOF exit 0 fi @@ -32,6 +36,7 @@ password="${TELEPHONY_TELEKOM_PASSWORD:-}" caller_id="${TELEPHONY_TELEKOM_CALLER_ID:-$sip_user}" inbound_extension="${TELEPHONY_TELEKOM_INBOUND_EXTENSION:-1001}" outbound_prefix="${TELEPHONY_TELEKOM_OUTBOUND_PREFIX:-0}" +external_media_address="${TELEPHONY_ASTERISK_EXTERNAL_MEDIA_ADDRESS:-${TELEPHONY_ASTERISK_EXTERNAL_SIGNALING_ADDRESS:-}}" if [ -z "$sip_user" ] || [ -z "$password" ]; then cat > "$PJSIP_FILE" <<'EOF' @@ -39,6 +44,9 @@ if [ -z "$sip_user" ] || [ -z "$password" ]; then EOF cat > "$EXTENSIONS_FILE" <<'EOF' ; Telekom-Anbindung ist aktiviert, aber nicht vollständig konfiguriert. +EOF + cat > "$PJSIP_WEBRTC_FILE" <<'EOF' +; Kein externes WebRTC-Media-Rewrite konfiguriert. EOF echo "FEDEO Telefonie: Telekom-Anbindung unvollständig, Trunk wird nicht erzeugt." >&2 exit 0 @@ -108,6 +116,10 @@ exten => _+X.,1,NoOp(FEDEO ausgehend über Telekom: \${EXTEN}) same => n,Hangup() [from-telekom] +exten => $caller_id,1,NoOp(FEDEO eingehend über Telekom DID: \${EXTEN}) + same => n,Dial(PJSIP/$inbound_extension,30) + same => n,Hangup() + exten => s,1,NoOp(FEDEO eingehend über Telekom) same => n,Dial(PJSIP/$inbound_extension,30) same => n,Hangup() @@ -117,4 +129,16 @@ exten => _X!,1,NoOp(FEDEO eingehend über Telekom: \${EXTEN}) same => n,Hangup() EOF +if [ -n "$external_media_address" ]; then + cat > "$PJSIP_WEBRTC_FILE" < "$PJSIP_WEBRTC_FILE" <<'EOF' +; Kein externes WebRTC-Media-Rewrite konfiguriert. +EOF +fi + echo "FEDEO Telefonie: Telekom-Trunk für $sip_user@$registrar erzeugt." diff --git a/telephony/vps-asterisk.env b/telephony/vps-asterisk.env index d129e45..4af05b2 100644 --- a/telephony/vps-asterisk.env +++ b/telephony/vps-asterisk.env @@ -5,6 +5,8 @@ TELEPHONY_ENABLED=true TELEPHONY_ASTERISK_HTTP_URL=http://188.245.76.1:8088/ws TELEPHONY_ASTERISK_WS_URL=ws://188.245.76.1:8088/ws TELEPHONY_SIP_DOMAIN=188.245.76.1 +TELEPHONY_ASTERISK_EXTERNAL_SIGNALING_ADDRESS=188.245.76.1 +TELEPHONY_ASTERISK_EXTERNAL_MEDIA_ADDRESS=188.245.76.1 TELEPHONY_ASTERISK_AMI_HOST=127.0.0.1 TELEPHONY_ASTERISK_AMI_PORT=5038 TELEPHONY_ASTERISK_AMI_USER=fedeo