KI-AGENT: NAT Einstellungen für Telekom-Trunk ergänzen

This commit is contained in:
2026-05-21 17:09:23 +02:00
parent beb91bf5c3
commit 7a893dfdcb
7 changed files with 87 additions and 6 deletions

View File

@@ -0,0 +1,3 @@
ALTER TABLE "telephony_trunks" ADD COLUMN IF NOT EXISTS "external_signaling_address" text;
ALTER TABLE "telephony_trunks" ADD COLUMN IF NOT EXISTS "external_media_address" text;
ALTER TABLE "telephony_trunks" ADD COLUMN IF NOT EXISTS "local_networks" text;

View File

@@ -323,6 +323,13 @@
"when": 1780164000000,
"tag": "0045_telephony_trunks",
"breakpoints": true
},
{
"idx": 46,
"version": "7",
"when": 1780167600000,
"tag": "0046_telephony_trunk_nat",
"breakpoints": true
}
]
}

View File

@@ -30,6 +30,9 @@ export const telephonyTrunks = pgTable(
callerId: text("caller_id"),
inboundExtension: text("inbound_extension").notNull().default("1001"),
outboundPrefix: text("outbound_prefix").notNull().default("0"),
externalSignalingAddress: text("external_signaling_address"),
externalMediaAddress: text("external_media_address"),
localNetworks: text("local_networks"),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()

View File

@@ -75,6 +75,9 @@ const sanitizeTrunk = (trunk: any) => ({
callerId: trunk?.callerId || "",
inboundExtension: trunk?.inboundExtension || "1001",
outboundPrefix: trunk?.outboundPrefix || "0",
externalSignalingAddress: trunk?.externalSignalingAddress || "",
externalMediaAddress: trunk?.externalMediaAddress || "",
localNetworks: trunk?.localNetworks || "172.16.0.0/12,192.168.0.0/16,10.0.0.0/8",
})
const envExternalTelephonyConfig = () => {
@@ -157,6 +160,7 @@ const renderTelekomPjsipConfig = (trunk: any) => {
const authUser = asteriskValue(trunk.authUser) || sipUser
const password = asteriskValue(trunk.password)
const callerId = asteriskValue(trunk.callerId) || sipUser
const externalMediaAddress = asteriskValue(trunk.externalMediaAddress || trunk.externalSignalingAddress)
return [
"; Von FEDEO generiert. Änderungen im Container können überschrieben werden.",
@@ -181,6 +185,7 @@ const renderTelekomPjsipConfig = (trunk: any) => {
`from_user=${sipUser}`,
`from_domain=${registrar}`,
`callerid=Telekom <${callerId}>`,
...(externalMediaAddress ? [`media_address=${externalMediaAddress}`] : []),
"direct_media=no",
"force_rport=yes",
"rewrite_contact=yes",
@@ -249,6 +254,35 @@ const renderTelekomExtensionsConfig = (trunk: any) => {
].join("\n")
}
const renderTelekomTransportConfig = (trunk: any) => {
const externalSignalingAddress = asteriskValue(trunk?.externalSignalingAddress)
const externalMediaAddress = asteriskValue(trunk?.externalMediaAddress || trunk?.externalSignalingAddress)
const localNetworks = asteriskValue(trunk?.localNetworks || "172.16.0.0/12,192.168.0.0/16,10.0.0.0/8")
.split(/[\s,;]+/)
.map((entry) => entry.trim())
.filter(Boolean)
if (!externalSignalingAddress && !externalMediaAddress) {
return [
"; Von FEDEO generiert.",
"; Kein externes Asterisk-NAT-Rewrite konfiguriert.",
"",
].join("\n")
}
return [
"; Von FEDEO generiert. Änderungen am Transport benötigen einen Asterisk-Neustart.",
"[transport-udp](+)",
...(externalSignalingAddress ? [
`external_signaling_address=${externalSignalingAddress}`,
"external_signaling_port=5060",
] : []),
...(externalMediaAddress ? [`external_media_address=${externalMediaAddress}`] : []),
...localNetworks.map((network) => `local_net=${network}`),
"",
].join("\n")
}
const writeAsteriskTrunkConfig = async (trunk: any) => {
const targetDir = asteriskGeneratedDir()
await fs.mkdir(targetDir, { recursive: true })
@@ -262,6 +296,10 @@ const writeAsteriskTrunkConfig = async (trunk: any) => {
name: "extensions.telekom.conf",
content: renderTelekomExtensionsConfig(trunk),
},
{
name: "pjsip.transport.conf",
content: renderTelekomTransportConfig(trunk),
},
]
await Promise.all(files.map(async (file) => {
@@ -364,10 +402,13 @@ export default async function telephonyRoutes(server: FastifyInstance) {
return {
enabled: trunk.enabled,
provider: trunk.provider,
inboundExtension: trunk.inboundExtension,
outboundPrefix: trunk.outboundPrefix,
registrar: trunk.registrar,
sipUserConfigured: Boolean(trunk.sipUser),
inboundExtension: trunk.inboundExtension,
outboundPrefix: trunk.outboundPrefix,
registrar: trunk.registrar,
externalSignalingAddress: trunk.externalSignalingAddress,
externalMediaAddress: trunk.externalMediaAddress,
localNetworks: trunk.localNetworks,
sipUserConfigured: Boolean(trunk.sipUser),
authUserConfigured: Boolean(trunk.authUser),
passwordConfigured: Boolean(trunk.password),
callerIdConfigured: Boolean(trunk.callerId),
@@ -463,6 +504,9 @@ export default async function telephonyRoutes(server: FastifyInstance) {
callerId: bodyString(body, "callerId"),
inboundExtension: bodyString(body, "inboundExtension") || "1001",
outboundPrefix: bodyString(body, "outboundPrefix") || "0",
externalSignalingAddress: bodyString(body, "externalSignalingAddress"),
externalMediaAddress: bodyString(body, "externalMediaAddress"),
localNetworks: bodyString(body, "localNetworks") || "172.16.0.0/12,192.168.0.0/16,10.0.0.0/8",
password: clearPassword ? null : (password || existing?.password || null),
updatedAt: now,
updatedBy: req.user.user_id,

View File

@@ -22,11 +22,16 @@ TELEPHONY_TELEKOM_AUTH_USER=<anschlusskennung><zugangsnummer>#<mitbenutzernummer
- `Absendernummer`: optional, meist identisch zur SIP-ID
- `Eingehende Nebenstelle`: lokale FEDEO/Asterisk-Nebenstelle, z. B. `1001`
- `Ausgehender Prefix`: standardmäßig `0`
- `Öffentliche Signaling-Adresse`: öffentliche IP oder DNS-Name, unter dem Asterisk von Telekom erreichbar ist
- `Öffentliche Medien-Adresse`: öffentliche RTP-Adresse, leer nutzt die Signaling-Adresse
- `Lokale Netze`: interne Netze, für die Asterisk keine öffentliche Adresse einsetzen soll
Das Kennwort wird nicht wieder an das Frontend zurückgegeben. In der Oberfläche siehst du nur, ob ein Kennwort gespeichert ist.
Nach dem Speichern muss die Konfiguration mit **In Asterisk anwenden** in die Asterisk-Include-Dateien geschrieben werden. FEDEO lädt danach PJSIP und den Dialplan über AMI neu und fordert die Telekom-Registration an. Den Laufzeitstatus findest du unter **Kommunikation -> Telefonie Setup** im Bereich **Externe Telefonie**.
Wenn du die öffentliche Signaling- oder Medien-Adresse änderst, muss Asterisk anschließend neu gestartet werden, weil PJSIP-Transporte diese Werte nicht vollständig im laufenden Betrieb neu laden.
## `.env` Fallback
Die `.env`-Werte bleiben nur als lokaler Fallback für den Asterisk-Teststack erhalten, falls keine mandantenbezogene Konfiguration gepflegt ist.

View File

@@ -145,7 +145,10 @@ const telephonyTrunkForm = reactive({
clearPassword: false,
callerId: "",
inboundExtension: "1001",
outboundPrefix: "0"
outboundPrefix: "0",
externalSignalingAddress: "",
externalMediaAddress: "",
localNetworks: "172.16.0.0/12,192.168.0.0/16,10.0.0.0/8"
})
const setupPage = async () => {
@@ -223,6 +226,9 @@ const loadTelephonyTrunk = async () => {
telephonyTrunkForm.callerId = res?.callerId || ""
telephonyTrunkForm.inboundExtension = res?.inboundExtension || "1001"
telephonyTrunkForm.outboundPrefix = res?.outboundPrefix || "0"
telephonyTrunkForm.externalSignalingAddress = res?.externalSignalingAddress || ""
telephonyTrunkForm.externalMediaAddress = res?.externalMediaAddress || ""
telephonyTrunkForm.localNetworks = res?.localNetworks || "172.16.0.0/12,192.168.0.0/16,10.0.0.0/8"
} catch (error) {
toast.add({ title: "Telefonie-Trunk konnte nicht geladen werden", color: "error" })
} finally {
@@ -254,7 +260,10 @@ const saveTelephonyTrunk = async () => {
clearPassword: telephonyTrunkForm.clearPassword,
callerId: telephonyTrunkForm.callerId,
inboundExtension: telephonyTrunkForm.inboundExtension,
outboundPrefix: telephonyTrunkForm.outboundPrefix
outboundPrefix: telephonyTrunkForm.outboundPrefix,
externalSignalingAddress: telephonyTrunkForm.externalSignalingAddress,
externalMediaAddress: telephonyTrunkForm.externalMediaAddress,
localNetworks: telephonyTrunkForm.localNetworks
}
})
@@ -523,6 +532,15 @@ onMounted(() => {
<UFormField label="Ausgehender Prefix">
<UInput v-model="telephonyTrunkForm.outboundPrefix" placeholder="0" />
</UFormField>
<UFormField label="Öffentliche Signaling-Adresse">
<UInput v-model="telephonyTrunkForm.externalSignalingAddress" placeholder="Öffentliche IP oder DNS-Name" />
</UFormField>
<UFormField label="Öffentliche Medien-Adresse">
<UInput v-model="telephonyTrunkForm.externalMediaAddress" placeholder="Leer = Signaling-Adresse" />
</UFormField>
<UFormField label="Lokale Netze">
<UInput v-model="telephonyTrunkForm.localNetworks" placeholder="172.16.0.0/12,192.168.0.0/16,10.0.0.0/8" />
</UFormField>
</div>
<div class="flex flex-wrap gap-2">

View File

@@ -6,6 +6,7 @@ user_agent=FEDEO Local Asterisk
type=transport
protocol=udp
bind=0.0.0.0:5060
#tryinclude generated/pjsip.transport.conf
[transport-ws]
type=transport