KI-AGENT: Asterisk-Stand ohne FreePBX versionieren

This commit is contained in:
2026-05-21 22:25:01 +02:00
parent cc34acac3e
commit 266c07d820
13 changed files with 66 additions and 117 deletions

View File

@@ -77,19 +77,9 @@ TELEPHONY_DEV_WS_PORT=8088
TELEPHONY_DEV_AMI_PORT=5038 TELEPHONY_DEV_AMI_PORT=5038
TELEPHONY_DEV_SIP_PORT=5060 TELEPHONY_DEV_SIP_PORT=5060
TELEPHONY_DEV_RTP_MIN_PORT=10000 TELEPHONY_DEV_RTP_MIN_PORT=10000
TELEPHONY_DEV_RTP_MAX_PORT=10020 TELEPHONY_DEV_RTP_MAX_PORT=10100
TELEPHONY_ASTERISK_EXTERNAL_SIGNALING_ADDRESS=
# Optionales FreePBX-Diagnoseprofil. Das Profil ist nur als Referenz-PBX für TELEPHONY_ASTERISK_EXTERNAL_MEDIA_ADDRESS=
# 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
# Externe Telefonie über Telekom/tel.t-online.de. Keine echten Zugangsdaten # Externe Telefonie über Telekom/tel.t-online.de. Keine echten Zugangsdaten
# einchecken. SIP-ID ist in der Regel die Rufnummer mit Vorwahl ohne Leerzeichen # einchecken. SIP-ID ist in der Regel die Rufnummer mit Vorwahl ohne Leerzeichen

View File

@@ -265,6 +265,7 @@ const renderProviderExtensionsConfig = (trunk: any) => {
const outboundPrefix = asteriskValue(trunk.outboundPrefix) || "0" const outboundPrefix = asteriskValue(trunk.outboundPrefix) || "0"
const escapedPrefix = outboundPrefix.replace(/[^0-9*#+]/g, "") const escapedPrefix = outboundPrefix.replace(/[^0-9*#+]/g, "")
const callerId = asteriskValue(trunk.callerId) || asteriskValue(trunk.sipUser) const callerId = asteriskValue(trunk.callerId) || asteriskValue(trunk.sipUser)
const inboundDid = callerId.replace(/[^0-9+]/g, "")
return [ return [
"; Von FEDEO generiert. Änderungen im Container können überschrieben werden.", "; Von FEDEO generiert. Änderungen im Container können überschrieben werden.",
@@ -282,6 +283,12 @@ const renderProviderExtensionsConfig = (trunk: any) => {
" same => n,Hangup()", " same => n,Hangup()",
"", "",
`[from-${providerKey}]`, `[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})`, `exten => s,1,NoOp(FEDEO eingehend über ${provider.label})`,
` same => n,Dial(PJSIP/${inboundExtension},30)`, ` same => n,Dial(PJSIP/${inboundExtension},30)`,
" same => n,Hangup()", " same => n,Hangup()",
@@ -293,6 +300,25 @@ const renderProviderExtensionsConfig = (trunk: any) => {
].join("\n") ].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 renderTelekomTransportConfig = (trunk: any) => {
const externalSignalingAddress = asteriskValue(trunk?.externalSignalingAddress) const externalSignalingAddress = asteriskValue(trunk?.externalSignalingAddress)
const externalMediaAddress = asteriskValue(trunk?.externalMediaAddress || trunk?.externalSignalingAddress) const externalMediaAddress = asteriskValue(trunk?.externalMediaAddress || trunk?.externalSignalingAddress)
@@ -339,6 +365,10 @@ const writeAsteriskTrunkConfig = async (trunk: any) => {
name: "pjsip.transport.conf", name: "pjsip.transport.conf",
content: renderTelekomTransportConfig(trunk), content: renderTelekomTransportConfig(trunk),
}, },
{
name: "pjsip.webrtc.conf",
content: renderWebRtcConfig(trunk),
},
] ]
await Promise.all(files.map(async (file) => { await Promise.all(files.map(async (file) => {

View File

@@ -125,6 +125,8 @@ services:
- TELEPHONY_TELEKOM_CALLER_ID=${TELEPHONY_TELEKOM_CALLER_ID:-} - TELEPHONY_TELEKOM_CALLER_ID=${TELEPHONY_TELEKOM_CALLER_ID:-}
- TELEPHONY_TELEKOM_INBOUND_EXTENSION=${TELEPHONY_TELEKOM_INBOUND_EXTENSION:-1001} - TELEPHONY_TELEKOM_INBOUND_EXTENSION=${TELEPHONY_TELEKOM_INBOUND_EXTENSION:-1001}
- TELEPHONY_TELEKOM_OUTBOUND_PREFIX=${TELEPHONY_TELEKOM_OUTBOUND_PREFIX:-0} - 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 - ASTERISK_GENERATED_DIR=/etc/asterisk/generated
command: command:
- /bin/sh - /bin/sh
@@ -138,50 +140,7 @@ services:
- "${TELEPHONY_DEV_WS_PORT:-8088}:8088" - "${TELEPHONY_DEV_WS_PORT:-8088}:8088"
- "${TELEPHONY_DEV_AMI_PORT:-5038}:5038" - "${TELEPHONY_DEV_AMI_PORT:-5038}:5038"
- "${TELEPHONY_DEV_SIP_PORT:-5060}:5060/udp" - "${TELEPHONY_DEV_SIP_PORT:-5060}:5060/udp"
- "${TELEPHONY_DEV_RTP_MIN_PORT:-10000}-${TELEPHONY_DEV_RTP_MAX_PORT:-10020}:10000-10020/udp" - "${TELEPHONY_DEV_RTP_MIN_PORT:-10000}-${TELEPHONY_DEV_RTP_MAX_PORT:-10100}:10000-10100/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"
networks: networks:
- traefik - traefik
@@ -532,19 +491,6 @@ services:
- "./traefik/logs:/logs" - "./traefik/logs:/logs"
networks: networks:
- traefik - 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: networks:
traefik: traefik:
external: false external: false

View File

@@ -66,38 +66,6 @@ Wenn `TELEPHONY_TELEKOM_AUTH_USER` leer bleibt, verwendet Asterisk automatisch `
docker compose --profile telephony-dev up -d asterisk-dev 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 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.
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.

View File

@@ -31,6 +31,7 @@ force_rport=yes
rewrite_contact=yes rewrite_contact=yes
rtp_symmetric=yes rtp_symmetric=yes
transport=transport-ws transport=transport-ws
#tryinclude generated/pjsip.webrtc.conf
from_domain=localhost from_domain=localhost
[1001](fedeo-webrtc) [1001](fedeo-webrtc)

View File

@@ -1,5 +1,5 @@
[general] [general]
rtpstart=10000 rtpstart=10000
rtpend=10020 rtpend=10100
icesupport=yes icesupport=yes
strictrtp=no strictrtp=no

View File

@@ -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;

View File

@@ -1,2 +0,0 @@
[mysqld]
sql_mode=NO_ENGINE_SUBSTITUTION

View File

@@ -1 +0,0 @@
change-this-freepbx-user-password

View File

@@ -1 +0,0 @@
change-this-freepbx-root-password

View File

@@ -1 +0,0 @@
[smtp.example.com]:587 user@example.com:change-this-app-password

View File

@@ -6,6 +6,7 @@ mkdir -p "$GENERATED_DIR"
PJSIP_FILE="$GENERATED_DIR/pjsip.telekom.conf" PJSIP_FILE="$GENERATED_DIR/pjsip.telekom.conf"
EXTENSIONS_FILE="$GENERATED_DIR/extensions.telekom.conf" EXTENSIONS_FILE="$GENERATED_DIR/extensions.telekom.conf"
PJSIP_WEBRTC_FILE="$GENERATED_DIR/pjsip.webrtc.conf"
enabled="${TELEPHONY_TELEKOM_ENABLED:-false}" enabled="${TELEPHONY_TELEKOM_ENABLED:-false}"
@@ -21,6 +22,9 @@ if [ "$enabled" != "true" ] && [ "$enabled" != "1" ] && [ "$enabled" != "yes" ];
EOF EOF
cat > "$EXTENSIONS_FILE" <<'EOF' cat > "$EXTENSIONS_FILE" <<'EOF'
; Telekom-Anbindung ist deaktiviert. ; Telekom-Anbindung ist deaktiviert.
EOF
cat > "$PJSIP_WEBRTC_FILE" <<'EOF'
; Kein externes WebRTC-Media-Rewrite konfiguriert.
EOF EOF
exit 0 exit 0
fi fi
@@ -32,6 +36,7 @@ password="${TELEPHONY_TELEKOM_PASSWORD:-}"
caller_id="${TELEPHONY_TELEKOM_CALLER_ID:-$sip_user}" caller_id="${TELEPHONY_TELEKOM_CALLER_ID:-$sip_user}"
inbound_extension="${TELEPHONY_TELEKOM_INBOUND_EXTENSION:-1001}" inbound_extension="${TELEPHONY_TELEKOM_INBOUND_EXTENSION:-1001}"
outbound_prefix="${TELEPHONY_TELEKOM_OUTBOUND_PREFIX:-0}" 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 if [ -z "$sip_user" ] || [ -z "$password" ]; then
cat > "$PJSIP_FILE" <<'EOF' cat > "$PJSIP_FILE" <<'EOF'
@@ -39,6 +44,9 @@ if [ -z "$sip_user" ] || [ -z "$password" ]; then
EOF EOF
cat > "$EXTENSIONS_FILE" <<'EOF' cat > "$EXTENSIONS_FILE" <<'EOF'
; Telekom-Anbindung ist aktiviert, aber nicht vollständig konfiguriert. ; Telekom-Anbindung ist aktiviert, aber nicht vollständig konfiguriert.
EOF
cat > "$PJSIP_WEBRTC_FILE" <<'EOF'
; Kein externes WebRTC-Media-Rewrite konfiguriert.
EOF EOF
echo "FEDEO Telefonie: Telekom-Anbindung unvollständig, Trunk wird nicht erzeugt." >&2 echo "FEDEO Telefonie: Telekom-Anbindung unvollständig, Trunk wird nicht erzeugt." >&2
exit 0 exit 0
@@ -108,6 +116,10 @@ exten => _+X.,1,NoOp(FEDEO ausgehend über Telekom: \${EXTEN})
same => n,Hangup() same => n,Hangup()
[from-telekom] [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) exten => s,1,NoOp(FEDEO eingehend über Telekom)
same => n,Dial(PJSIP/$inbound_extension,30) same => n,Dial(PJSIP/$inbound_extension,30)
same => n,Hangup() same => n,Hangup()
@@ -117,4 +129,16 @@ exten => _X!,1,NoOp(FEDEO eingehend über Telekom: \${EXTEN})
same => n,Hangup() same => n,Hangup()
EOF EOF
if [ -n "$external_media_address" ]; then
cat > "$PJSIP_WEBRTC_FILE" <<EOF
; Automatisch aus Umgebungsvariablen erzeugt. Nicht in Git einchecken.
media_address=$external_media_address
bind_rtp_to_media_address=yes
EOF
else
cat > "$PJSIP_WEBRTC_FILE" <<'EOF'
; Kein externes WebRTC-Media-Rewrite konfiguriert.
EOF
fi
echo "FEDEO Telefonie: Telekom-Trunk für $sip_user@$registrar erzeugt." echo "FEDEO Telefonie: Telekom-Trunk für $sip_user@$registrar erzeugt."

View File

@@ -5,6 +5,8 @@ TELEPHONY_ENABLED=true
TELEPHONY_ASTERISK_HTTP_URL=http://188.245.76.1:8088/ws TELEPHONY_ASTERISK_HTTP_URL=http://188.245.76.1:8088/ws
TELEPHONY_ASTERISK_WS_URL=ws://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_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_HOST=127.0.0.1
TELEPHONY_ASTERISK_AMI_PORT=5038 TELEPHONY_ASTERISK_AMI_PORT=5038
TELEPHONY_ASTERISK_AMI_USER=fedeo TELEPHONY_ASTERISK_AMI_USER=fedeo