Use Cloudflare 1.1.1.1/cdn-cgi/trace as primary IP source (fixed IP, no resolver dependency — survives AdGuard/upstream DNS outages). Add calibre to DNS_RECORDS: its A record points at the dynamic WAN IP but was not guarded, so it would go stale on the next WAN change.
168 lines
6.2 KiB
Markdown
168 lines
6.2 KiB
Markdown
# WAN IP Guard
|
|
|
|
Bewaakt het publieke WAN IP en update automatisch DNS records bij wijziging, plus WhatsApp notificatie.
|
|
|
|
## Wat doet het?
|
|
|
|
- Controleert elke 5 minuten het publieke WAN IP
|
|
- Bij wijziging:
|
|
- Updatet A-records via de DirectAdmin API
|
|
- Stuurt een WhatsApp notificatie naar de admin
|
|
- Draait als systemd timer
|
|
|
|
## Installatie
|
|
|
|
### 1. Clone op je server
|
|
|
|
```bash
|
|
cd ~
|
|
git clone <jouw-repo-url> wan-ip-guard
|
|
cd wan-ip-guard
|
|
```
|
|
|
|
### 2. Configureer
|
|
|
|
```bash
|
|
# .env naar /etc (nodig vanwege systemd beperkingen op nieuwere Fedora)
|
|
sudo mkdir -p /etc/wan-ip-guard
|
|
sudo cp .env.example /etc/wan-ip-guard/.env
|
|
sudo nano /etc/wan-ip-guard/.env
|
|
```
|
|
|
|
### 3. Installeer dependencies
|
|
|
|
```bash
|
|
npm install --omit=dev
|
|
```
|
|
|
|
### 4. Systemd setup
|
|
|
|
```bash
|
|
# State directory
|
|
sudo mkdir -p /var/lib/wan-ip-guard
|
|
sudo chown $USER:$USER /var/lib/wan-ip-guard
|
|
|
|
# Pas de paden in systemd/*.service aan naar jouw setup
|
|
# (gebruiker, WorkingDirectory, ExecStart)
|
|
|
|
# Systemd units
|
|
sudo cp systemd/wan-ip-guard.service /etc/systemd/system/
|
|
sudo cp systemd/wan-ip-guard.timer /etc/systemd/system/
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable --now wan-ip-guard.timer
|
|
```
|
|
|
|
## Configuratie (.env)
|
|
|
|
| Variabele | Beschrijving | Voorbeeld |
|
|
|-----------|-------------|-----------|
|
|
| `DA_LOGIN_KEY` | DirectAdmin Login Key | `your_login_key` |
|
|
| `DA_USERNAME` | DirectAdmin gebruiker | `your_username` |
|
|
| `DA_BASE_URL` | DirectAdmin base URL | `https://panel.example.com:2222` |
|
|
| `DA_DOMAIN` | Domeinnaam | `example.com` |
|
|
| `WHATSAPP_API_ENDPOINT` | wwebjs-api URL | `http://localhost:3001` |
|
|
| `WHATSAPP_SESSION` | WhatsApp sessie naam | `your_session` |
|
|
| `ADMIN_PHONE` | Admin telefoonnummer | `31600000000` |
|
|
| `MATRIX_API_ENDPOINT` | Matrix homeserver API | `https://whatsed.eddydevink.nl` |
|
|
| `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,whatsed,auth.whatsed,calibre` |
|
|
| `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.
|
|
|
|
## DirectAdmin Login Key aanmaken
|
|
|
|
1. Log in op het DirectAdmin panel
|
|
2. Ga naar **Accountbeheer** → **Login Keys**
|
|
3. Maak een nieuwe key aan met rechten voor **DNS Control**
|
|
4. Sla de key op (het wordt maar één keer getoond)
|
|
|
|
## Beheer
|
|
|
|
```bash
|
|
# Timer status
|
|
systemctl status wan-ip-guard.timer
|
|
|
|
# Logs volgen
|
|
journalctl -u wan-ip-guard -f
|
|
|
|
# Handmatig draaien
|
|
cd ~/wan-ip-guard && node index.js
|
|
|
|
# Timer herstarten
|
|
sudo systemctl restart wan-ip-guard.timer
|
|
|
|
# State bestand resetten (forceer update bij volgende run)
|
|
rm /var/lib/wan-ip-guard/current_ip
|
|
```
|
|
|
|
## Probleemoplossing
|
|
|
|
### "Failed to load environment files: Permission denied"
|
|
|
|
Systemd (met name versie 259+ op Fedora 44) kan soms geen `EnvironmentFile` lezen vanuit een user home directory met beperkte rechten (`drwx------`).
|
|
|
|
**Symptoom:** Timer is actief, maar de service faalt elke run met:
|
|
```
|
|
wan-ip-guard.service: Failed to load environment files: Permission denied
|
|
wan-ip-guard.service: Failed to spawn 'start' task: Permission denied
|
|
```
|
|
|
|
**Oplossing:** Verplaats `.env` naar `/etc/wan-ip-guard/.env` (zoals de installatie instructies voorschrijven). Systemd kan bestanden in `/etc` altijd lezen ongeacht user-rechten.
|
|
|
|
## Hoe het werkt
|
|
|
|
1. **IP detectie:** Haalt het WAN IP op via `https://1.1.1.1/cdn-cgi/trace` (Cloudflare, DNS-vrij — werkt als de lokale resolver uitvalt) met fallback naar `ifconfig.me`, `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=<recordnaam>&value=<oude-ip>` in parameter `arecs0`. De waarde moet dus niet `recordnaam=ip` zijn.
|
|
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=<fqdn>&myip=<ip>` 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
|
|
|
|
De bewezen API-vorm voor één exact A-record is:
|
|
|
|
```text
|
|
CMD_API_DNS_CONTROL?domain=eddydevink.nl&action=select&arecs0=<urlencoded name=<recordnaam>&value=<oude-ip>>
|
|
```
|
|
|
|
Voorbeeld vóór URL-encoding:
|
|
|
|
```text
|
|
name=changeme&value=94.157.123.170
|
|
```
|
|
|
|
Encodeer het volledige `name=...&value=...`-paar. Controleer na de API-call de zone opnieuw; een API-antwoord `Records verwijderd` is onvoldoende bewijs wanneer het oude record nog in de zone staat.
|
|
|
|
## Files
|
|
|
|
```
|
|
wan-ip-guard/
|
|
├── index.js # Hoofdscript
|
|
├── package.json
|
|
├── .env.example # Config template
|
|
├── deploy.sh # Deployment script
|
|
├── systemd/
|
|
│ ├── wan-ip-guard.service
|
|
│ └── wan-ip-guard.timer
|
|
```
|
|
|
|
## Serverstructuur (nextcloud)
|
|
|
|
```
|
|
/etc/wan-ip-guard/
|
|
├── .env # Config (door root beheerd)
|
|
|
|
/var/lib/wan-ip-guard/
|
|
├── current_ip # Huidig opgeslagen IP
|
|
|
|
/home/aiaiaiaiagent/wan-ip-guard/
|
|
├── index.js # Broncode
|
|
├── node_modules/
|
|
└── ...
|
|
```
|