Files
bf-infra/routeros/tp10-ddns-cf-updater.rsc
Christoph Emsenhuber 6e2a7cfb47 TP10: Global-Scope-Fix für cfDdnsToken im DDNS-Updater (RouterOS 7.24 Skript-Scope vs CLI-Scope)
Problem: :global cfDdnsToken liest im Skript-Kontext 'nothing', obwohl
/system script environment cfDdnsToken gesetzt ist (53 Zeichen, live verifiziert).

Fix: /system script environment get statt :global — scope-unabhängig auf ROS 7.24.
Skript importiert als tp10-ddns-bf4, Trigger: DHCP-Client client1 (Lease-Event),
kein stündlicher Scheduler nötig (Entscheidung 2026-10-03).

Recovery-Lücke dokumentiert: GC-Template rendert kein /system script /
/system scheduler — Recovery von null hätte Updater nicht.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-03 17:38:55 +02:00

113 lines
5.3 KiB
Plaintext

# =============================================================================
# TP10 — Cloudflare-DDNS-Updater fuer bf4.emsenhuber.net
# =============================================================================
# Zweck: der WG-Hub-Endpoint der Spokes (fm/dw) ist bf4.emsenhuber.net. Der
# A-Record muss der Liwest-Public-IP des Core folgen. Heute manuell gepflegt.
#
# BASIS: bajodel/mikrotik-cloudflare-dns (MIT, Copyright (c) 2025 bajodel)
# https://github.com/bajodel/mikrotik-cloudflare-dns
# Aenderungen ggue. dem Original (Christoph-Entscheid 2026-10-03):
# 1. IP kommt aus der DHCP-Lease (client1/vlan100), NICHT von Echo-Diensten.
# Liwest ist die Quelle, kein Dritter; kein Cloudflare im Pfad.
# 2. check-certificate=yes (Original-Default war false -> Token ungeprueft).
# 3. Nur EIN Record (bf4), Konfiguration auf das Noetige reduziert.
#
# Token: liegt NICHT hier, sondern als globale Script-Variable `cfDdnsToken`.
# Einmalig setzen per SSH (siehe docs/teilprojekte.md ### TP10).
#
# Installation: /import file=tp10-ddns-cf-updater.rsc
# danach /system scheduler add ... (siehe unten)
# =============================================================================
# ---- Konfiguration ----------------------------------------------------------
:local DnsRcName "bf4.emsenhuber.net"
:local DnsZoneID "1b5bc02fdf61b6b989afde543f3f4134" # CF_ZONE_ID aus mcp/cloudflare/.env
:local DnsRcrdID "dba9d9ec3b261fdb314c0f83e4e62f0d"
:local RecordTTL "120" # wie der bestehende Record
:local RecordProxied "false" # WG-Endpoint: NICHT proxied
:local VerboseLog false
:local WanDhcpName "client1"
# -----------------------------------------------------------------------------
# Token aus /system script environment holen (NIE im Skriptkopf, NIE per :global).
# Grund (2026-10-03): :global cfDdnsToken liest im Skript-Kontext "nothing", obwohl
# /system script environment cfDdnsToken gesetzt ist. CLI-Scope und Skript-Scope sind
# auf RouterOS 7.24 unterschiedlich. `/system script environment get` ist scope-unabhaengig.
:do {
:local tk ""
:local found [/system script environment find name=cfDdnsToken]
:if ([:len $found] > 0) do={
:set tk [/system script environment get [find name=cfDdnsToken] value]
}
:if ([:typeof $tk] = "") do={
:log error "[tp10-ddns] cfDdnsToken nicht gesetzt - Abbruch"
:error "cfDdnsToken fehlt"
}
:global cfDdnsToken
:set cfDdnsToken $tk
} on-error={
:log error "[tp10-ddns] cfDdnsToken nicht lesbar - Abbruch"
:error "cfDdnsToken fehlt"
}
# ---- WAN-IPv4 aus der DHCP-Lease (Quelle: Liwest, kein Dritter) ------------
:local CFWanIP4New ""
:do {
:local leaseAddr [/ip dhcp-client get [find name=$WanDhcpName] address]
:if ([:typeof $leaseAddr] != "nothing") do={
# "90.146.33.177/24" -> "90.146.33.177"
:local slashPos [:find $leaseAddr "/"]
:if ($slashPos >= 0) do={
:set CFWanIP4New [:pick $leaseAddr 0 $slashPos]
} else={
:set CFWanIP4New $leaseAddr
}
}
} on-error={
:log error "[tp10-ddns] DHCP-Lease nicht lesbar ($WanDhcpName)"
}
:if ($CFWanIP4New = "") do={
:log warning "[tp10-ddns] Keine WAN-IP aus der Lease, naechster Lauf"
} else={
# ---- Guard: nur oeffentliche IPv4 zulassen --------------------------------
# Faengt den Fall ab, dass DHCP Muell liefert (privat/CGNAT) -> kein Write.
:local isPrivate false
:if ([:pick $CFWanIP4New 0 3] = "10.") do={ :set isPrivate true }
:if ([:pick $CFWanIP4New 0 8] = "192.168.") do={ :set isPrivate true }
:if ([:pick $CFWanIP4New 0 7] = "172.16.") do={ :set isPrivate true }
:if ([:pick $CFWanIP4New 0 8] = "100.64.0") do={ :set isPrivate true } # CGNAT 100.64/10
:if ($isPrivate) do={
:log error "[tp10-ddns] Lease liefert nicht-oeffentliche IP ($CFWanIP4New) - ABBRUCH, kein Write"
} else={
# ---- Nur bei Abweichung schreiben (Idempotenz) --------------------------
:global CFWanIP4Cur
:if ($CFWanIP4New != $CFWanIP4Cur) do={
:if ($VerboseLog) do={ :log info "[tp10-ddns] WAN-IP: $CFWanIP4Cur -> $CFWanIP4New" }
# Kommentar am Record: Zeit + Geraet, wie im Original
:local ds [/system clock get date]
:local ts [/system clock get time]
:local datetime ([:pick $ds 0 4] . "." . [:pick $ds 5 7] . "." . [:pick $ds 8 10] . "-" . [:pick $ts 0 2] . ":" . [:pick $ts 3 5])
:local CFComment ("$datetime by crs326-core")
:local url "https://api.cloudflare.com/client/v4/zones/$DnsZoneID/dns_records/$DnsRcrdID/"
:do {
:local res [/tool fetch http-method=put mode=https url=$url check-certificate=yes output=user as-value http-header-field="Authorization: Bearer $cfDdnsToken,Content-Type: application/json" http-data="{\"type\":\"A\",\"name\":\"$DnsRcName\",\"content\":\"$CFWanIP4New\",\"ttl\":$RecordTTL,\"proxied\":$RecordProxied,\"comment\":\"$CFComment\"}"]
:if (($res->"status") = "finished") do={
:log warning "[tp10-ddns] $DnsRcName -> $CFWanIP4New aktualisiert"
:set CFWanIP4Cur $CFWanIP4New
} else={
:log error "[tp10-ddns] Update fehlgeschlagen (status != finished)"
}
} on-error={
:log error "[tp10-ddns] Exception beim Cloudflare-Update - naechster Lauf"
}
} else={
:if ($VerboseLog) do={ :log info "[tp10-ddns] WAN-IP unveraendert ($CFWanIP4New)" }
}
}
}