From b8e865c83e509655b5128ca8d7572687bc5b8ad5 Mon Sep 17 00:00:00 2001 From: Eddy de Vink Date: Fri, 25 Sep 2026 10:03:28 +0200 Subject: [PATCH] feat: log eddydev.ink via EuroDynDNS in WAN IP Guard - Voeg EURODYNDNS_HOSTS/USER/PASS config toe en update de A-records van EuroDNS-zone-domeinen via de EuroDynDNS dyndns2 endpoint bij IP-wijziging. - Resultaten mee in rapport; state alleen saven als ALLE updates (DirectAdmin + EuroDynDNS) slagen. - Voor eddydev.ink/www.eddydev.ink na stale DNS -> failed certbot renew (HTTP-01 via oud WAN IP); zie vault Nginx notitie. --- .env.example | 6 ++++++ README.md | 8 ++++++-- index.js | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index b8b91e5..7e8b596 100644 --- a/.env.example +++ b/.env.example @@ -17,5 +17,11 @@ MATRIX_ROOM_FILE=/home/aiaiaiaiagent/whatsed-new/secrets/eddy-report-room # DNS records to update (comma-separated, use domain name for apex) DNS_RECORDS=example.com,git,www,weegschaal,whatsed,auth.whatsed +# EuroDynDNS (domeinen in de EuroDNS-zone, bv. eddydev.ink) +# Gebruikt de EuroDNS site-login / site-password (dyndns2), NIET een API-key. +EURODYNDNS_USER=your_eurodns_site_login +EURODYNDNS_PASS=your_eurodns_site_password +EURODYNDNS_HOSTS=eddydev.ink,www.eddydev.ink + # State file path (where to store last known IP) STATE_FILE=/var/lib/wan-ip-guard/current_ip diff --git a/README.md b/README.md index 9100c92..3aaf522 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,9 @@ sudo systemctl enable --now wan-ip-guard.timer | `MATRIX_TOKEN_FILE` | Bestand met Sherpa compat-token | `/home/aiaiaiaiagent/whatsed-new/secrets/sherpa-token` | | `MATRIX_ROOM_FILE` | Bestand met DM-room voor Eddy | `/home/aiaiaiaiagent/whatsed-new/secrets/eddy-report-room` | | `DNS_RECORDS` | Komma-gescheiden records | `example.com,git,www` | +| `EURODYNDNS_USER` | EuroDNS site-login (dyndns2) | `your_login` | +| `EURODYNDNS_PASS` | EuroDNS site-password (dyndns2) | `your_password` | +| `EURODYNDNS_HOSTS` | EuroDynDNS-hostnames (domeinen in EuroDNS-zone) | `eddydev.ink,www.eddydev.ink` | | `STATE_FILE` | Pad naar state file | `/var/lib/wan-ip-guard/current_ip` | WAN IP Guard verstuurt het rapport naast WhatsApp ook via de Matrix-bot `@sherpa:whatsed.eddydevink.nl` naar de bestaande DM-room met `@eddy:whatsed.eddydevink.nl`. Het compat-token en room-ID staan uitsluitend in de aangegeven bestanden en worden niet in de repository opgeslagen. @@ -116,8 +119,9 @@ wan-ip-guard.service: Failed to spawn 'start' task: Permission denied 1. **IP detectie:** Haalt het WAN IP op via `ifconfig.me` met fallback naar `api.ipify.org` en `icanhazip.com` 2. **State vergelijking:** Vergelijkt met het laatst opgeslagen IP in de state file 3. **DNS update:** Voor elke record worden eerst alle bestaande A-records met de exacte oude waarde verwijderd en daarna één nieuw A-record toegevoegd via DirectAdmin API. De delete-aanroep gebruikt `action=select` en een URL-gecodeerde waarde `name=&value=` in parameter `arecs0`. De waarde moet dus niet `recordnaam=ip` zijn. -4. **State persist:** Het nieuwe IP wordt pas opgeslagen als **alle** DNS-updates gelukt zijn. Bij gedeeltelijke mislukking retried de volgende timer-run. -5. **Notificatie:** Stuurt WhatsApp bericht met oud/nieuw IP en status per record. +4. **EuroDynDNS:** Hostnames in `EURODYNDNS_HOSTS` (domeinen die in de EuroDNS-zone staan, bv. `eddydev.ink`) worden bij een IP-wijziging via de EuroDynDNS-update-URL bijgewerkt: `https://update.eurodyndns.org/update/?hostname=&myip=` met HTTP Basic auth (`EURODYNDNS_USER`/`EURODYNDNS_PASS`, de EuroDNS site-login/password). Een antwoord `good`/`nochg` telt als succes; een foutcode (`badauth`, `!yours`, `notfqdn`, …) als mislukking. +5. **State persist:** Het nieuwe IP wordt pas opgeslagen als **alle** DNS-updates (DirectAdmin én EuroDynDNS) gelukt zijn. Bij gedeeltelijke mislukking retried de volgende timer-run. +6. **Notificatie:** Stuurt WhatsApp bericht met oud/nieuw IP en status per record. ### DirectAdmin A-record verwijderen diff --git a/index.js b/index.js index eebb4c5..516fc36 100755 --- a/index.js +++ b/index.js @@ -19,6 +19,9 @@ const CONFIG = { matrixTokenFile: process.env.MATRIX_TOKEN_FILE || '/home/aiaiaiaiagent/whatsed-new/secrets/sherpa-token', matrixRoomFile: process.env.MATRIX_ROOM_FILE || '/home/aiaiaiaiagent/whatsed-new/secrets/eddy-report-room', dnsRecords: (process.env.DNS_RECORDS || '').split(',').map(s => s.trim()).filter(Boolean), + euroDynDnsUser: process.env.EURODYNDNS_USER, + euroDynDnsPass: process.env.EURODYNDNS_PASS, + euroDynDnsHosts: (process.env.EURODYNDNS_HOSTS || '').split(',').map(s => s.trim()).filter(Boolean), }; function log(msg) { @@ -117,6 +120,17 @@ async function addDnsRecord(name, ip) { await daRequest(endpoint); } +async function updateEuroDynDns(hostname, ip) { + const url = `https://update.eurodyndns.org/update/?hostname=${encodeURIComponent(hostname)}&myip=${encodeURIComponent(ip)}`; + const auth = Buffer.from(`${CONFIG.euroDynDnsUser}:${CONFIG.euroDynDnsPass}`).toString('base64'); + const resp = await fetchWithTimeout(url, { + headers: { Authorization: `Basic ${auth}` }, + }); + const text = (await resp.text()).trim(); + const ok = resp.ok && /^(good|nochg)\b/.test(text); + return { ok, text }; +} + async function sendWhatsApp(message) { const url = `${CONFIG.whatsappApi}/client/sendMessage/${CONFIG.whatsappSession}`; const chatId = `${CONFIG.adminPhone}@c.us`; @@ -205,6 +219,28 @@ async function main() { } } + // EuroDynDNS (domeinen in de EuroDNS-zone, bv. eddydev.ink) + if (CONFIG.euroDynDnsHosts.length > 0 && !(CONFIG.euroDynDnsUser && CONFIG.euroDynDnsPass)) { + logError('EURODYNDNS_HOSTS is set but EURODYNDNS_USER/PASS are missing. Skipping EuroDynDNS updates.'); + results.push(`❌ EuroDynDNS: missing EURODYNDNS_USER/PASS`); + } else { + for (const hostname of CONFIG.euroDynDnsHosts) { + try { + const { ok, text } = await updateEuroDynDns(hostname, currentIp); + if (ok) { + results.push(`✅ ${hostname} (EuroDynDNS)`); + log(`EuroDynDNS updated ${hostname} -> ${currentIp}: ${text}`); + } else { + results.push(`❌ ${hostname} (EuroDynDNS): ${text}`); + logError(`EuroDynDNS failed for ${hostname}: ${text}`); + } + } catch (e) { + results.push(`❌ ${hostname} (EuroDynDNS): ${e.message}`); + logError(`EuroDynDNS error for ${hostname}: ${e.message}`); + } + } + } + const successCount = results.filter(r => r.startsWith('✅')).length; const failCount = results.filter(r => r.startsWith('❌')).length;