Add README and remove hardcoded resolver

This commit is contained in:
2025-10-07 14:57:55 +02:00
parent 782d3c83dc
commit aaa1894a01
2 changed files with 150 additions and 1 deletions

150
README.md Normal file
View File

@@ -0,0 +1,150 @@
# Technitium DDNS
Python utility that fetches the host's public IPv4 and IPv6 and updates `A` and `AAAA` records for a given hostname in a zone hosted on a Technitium DNS server.
This repository contains a single service that supports two update backends:
- **HTTP_API** — use Technitium's HTTP API (token-based) to add/overwrite records.
- **RFC2136** — use standard DNS dynamic updates (RFC2136), optionally authenticated with a TSIG key.
The service runs in a continuous loop by default and updates every `UPDATE_INTERVAL` seconds (configurable via environment variables).
## Quick start
1. Create `.env` and fill in secrets and other variables.
2. Build and run:
```bash
docker compose build
docker compose up -d
```
Or to run with Docker directly:
```bash
docker build -t technitium-ddns .
docker run -d --name technitium-ddns --env-file .env technitium-ddns
```
## Environment variables
You can set these in `.env`, via `docker-compose.yml`, or when running the container.
General:
- `LOG_LEVEL` - `DEBUG`, `INFO`, `WARNING`, `ERROR` or `FATAL`.
- `LOG_FILE` - path of the log file or empty to log to stdout/stderr.
- `BACKEND``HTTP_API` (default) or `RFC2136`.
- `RUN_ONCE``1` to run a single update and exit, `0` to loop continuously (default `0`).
- `UPDATE_INTERVAL` — seconds between updates when looping (default `300`).
- `ZONE` — DNS zone to update the record into.
- `RECORD_NAME` — record name (left part) — the script updates `<RECORD_NAME>.<ZONE>`.
- `TTL` — record TTL in seconds (default `300`).
- `SKIP_IPV6` — set to `1` to skip AAAA updates.
HTTP API backend (Technitium):
- `TDNS_HOST` — Technitium base URL.
- `TDNS_API_TOKEN` — API token generated in Technitium web console (**required** for `HTTP_API`).
RFC2136 backend:
- `RFC2136_SERVER` — authoritative DNS server IP or hostname for dynamic updates (required for `RFC2136`).
- `RFC2136_PORT` — port (default `53`).
- `RFC2136_TSIG_NAME` — optional TSIG key name.
- `RFC2136_TSIG_KEY` — optional TSIG key (base64).
- `RFC2136_TSIG_ALGO` — optional TSIG algorithm (default `hmac-sha256.`).
## Example `.env`
```ini
# Log level and log file
LOG_LEVEL=INFO
LOG_FILE=/var/log/technitium-ddns.log
# Choose HTTP_API or RFC2136
BACKEND=HTTP_API
# If RUN_ONCE=1 the container runs once and exits (good for host cron).
RUN_ONCE=0
UPDATE_INTERVAL=300
ZONE=example.com
RECORD_NAME=ddns
TTL=300
SKIP_IPV6=0
# HTTP API (Technitium)
TDNS_HOST=http://technitium:5380
TDNS_API_TOKEN=YOUR_TECHNITIUM_TOKEN_HERE
# RFC2136 (if using RFC2136 backend)
RFC2136_SERVER=192.0.2.5
RFC2136_PORT=53
# Optional TSIG:
RFC2136_TSIG_NAME=ddns-key
RFC2136_TSIG_KEY=base64encodedkey==
RFC2136_TSIG_ALGO=hmac-sha256.
```
## How it behaves
- On each run (single-run or loop iteration) the script:
1. Detects public IPv4 (via `api.ipify.org`, fallback to other services).
2. Detects public IPv6 (unless `SKIP_IPV6=1`).
3. Queries current `A` / `AAAA` values; if they already match the current public IP, update is skipped.
4. Otherwise updates the record(s) using the selected backend.
- `HTTP_API` backend calls Technitium endpoint `/api/zones/records/add` with `overwrite=true`.
- `RFC2136` backend issues a dynamic DNS update packet and can use TSIG for authentication.
## Running as a cron job on the host (recommended for many setups)
If you prefer not to run a continuously-looping container, set `RUN_ONCE=1` and run the container from a host cron or systemd timer:
Example host systemd timer unit (recommended):
```ini
# /etc/systemd/system/tdns-updater-run.service
[Unit]
Description=Run tdns-updater once
[Service]
Type=oneshot
WorkingDirectory=/path/to/project
ExecStart=/usr/bin/docker compose run --rm tdns-updater
```
```ini
# /etc/systemd/system/tdns-updater-run.timer
[Unit]
Description=Run tdns-updater every 5 minutes
[Timer]
OnBootSec=1min
OnUnitActiveSec=5min
[Install]
WantedBy=timers.target
```
## RFC2136 notes
- Many servers expect dynamic updates to come from an allowed IP or require TSIG authentication. If using Technitium as the RFC2136 target, check its dynamic update / ACL settings and configure TSIG if required.
- The script supports TSIG via `RFC2136_TSIG_NAME` and `RFC2136_TSIG_KEY` (base64). Provide `RFC2136_TSIG_ALGO` like `hmac-sha256.` if needed.
## TLS / self-signed certs
If your `TDNS_HOST` is `https://` with a self-signed certificate, mount CA certificate(s) into the container and set `REQUESTS_CA_BUNDLE` or `SSL_CERT_FILE` appropriately, or use valid certificates to avoid modifying the image:
Example `docker-compose.yml` snippet for a CA bundle file:
```yaml
volumes:
- ./my-ca.pem:/usr/local/share/ca-certificates/my-ca.crt:ro
```
(then update CA store inside the image at build time, or pass `REQUESTS_CA_BUNDLE` pointing to the file.)
## Security
- Treat `TDNS_API_TOKEN` and `RFC2136_TSIG_KEY` as secrets. Do not commit them to VCS.
- For production, use Docker secrets, environment injection from your orchestrator, or mount a read-only file containing secrets.

View File

@@ -157,7 +157,6 @@ def rfc2136_update(record_type: str, ip: str) -> Tuple[bool, Any]:
def query_current_records(record_type: str) -> list: def query_current_records(record_type: str) -> list:
try: try:
resolver = dns.resolver resolver = dns.resolver
resolver.nameservers = ['192.168.178.2']
answers = resolver.resolve(FULL_DOMAIN, record_type, lifetime=5) answers = resolver.resolve(FULL_DOMAIN, record_type, lifetime=5)
return [r.to_text() for r in answers] return [r.to_text() for r in answers]
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer, dns.resolver.Timeout, dns.resolver.NoNameservers): except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer, dns.resolver.Timeout, dns.resolver.NoNameservers):