Skip to content

Commit 6df3943

Browse files
authored
Merge pull request #9 from worryboy/feature/containerize-schlundtech
Feature/containerize schlundtech
2 parents 5157066 + f754a23 commit 6df3943

22 files changed

Lines changed: 266 additions & 66 deletions

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.git
22
.env
3+
.env.dns
34
state/*
45
!state/.gitkeep
56
logs/

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
# Changelog
22

3-
## 0.4.0 - Traefik/CrowdSec Example
3+
## 0.4.0 - Provider Interface And Traefik/CrowdSec Example
44

5+
- Separated generic worker flow from DNS provider/interface code.
6+
- Made the current provider/interface explicit: InterNetX / AutoDNS / SchlundTech-related DNS via the InterNetX XML interface.
7+
- Added InterNetX XML as the first concrete provider implementation behind the provider contract.
8+
- Added provider documentation for InterNetX XML `auth_session` configuration and limits.
9+
- Added a lightweight verification design note for provider checks, optional public resolver checks, and optional local resolver checks.
510
- Added a dedicated Traefik/CrowdSec DynDNS example for one-host / many-hostname deployments.
611
- Added `.env.dns.example` and wired the example compose file to `.env.dns` to keep DNS settings separate from the Traefik/CrowdSec stack `.env`.
712
- Documented how the example extends, but does not replace, the goNeuland Traefik/CrowdSec guide and migration note.

Dockerfile

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,19 @@ LABEL org.opencontainers.image.title="InterNetX DynDNS" \
77
org.opencontainers.image.version="${APP_VERSION}" \
88
org.opencontainers.image.vendor="worryboy"
99

10-
RUN apk add --no-cache \
11-
ca-certificates \
12-
libcurl \
13-
libxml2 \
14-
&& apk add --no-cache --virtual .build-deps \
10+
WORKDIR /app
11+
12+
RUN set -eux; \
13+
apk add --no-cache --virtual .phpize-deps \
1514
$PHPIZE_DEPS \
1615
curl-dev \
17-
libxml2-dev \
18-
&& docker-php-ext-install curl dom \
19-
&& apk del .build-deps
20-
21-
WORKDIR /app
16+
libxml2-dev; \
17+
docker-php-ext-install -j"$(nproc)" curl dom; \
18+
apk add --no-cache \
19+
ca-certificates \
20+
libcurl \
21+
libxml2; \
22+
apk del --no-network .phpize-deps
2223

2324
COPY . /app
2425

README.Docker.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,15 @@
22

33
Containerized InterNetX DynDNS worker for IPv4/IPv6-aware DNS updates.
44

5-
This image runs as a long-lived outbound-only worker. It detects the current public IPv4 and/or IPv6 address once per cycle, validates one configured `TARGET_HOST` or multiple `TARGET_HOSTS`, and updates existing `A` and `AAAA` records through the InterNetX/AutoDNS XML API when the address changes.
5+
This image runs as a long-lived outbound-only worker. It detects the current public IPv4 and/or IPv6 address once per cycle, validates one configured `TARGET_HOST` or multiple `TARGET_HOSTS`, and updates existing `A` and `AAAA` records through the current DNS provider interface when the address changes.
66

77
Current release: `0.4.0`
88

9+
Current provider: InterNetX / AutoDNS / SchlundTech-related DNS.
10+
Current interface: InterNetX XML with `auth_session`.
11+
12+
The worker core is separated from provider/interface code. Provider-specific configuration and limits for the current InterNetX XML support live in [docs/providers/internetx-xml.md](docs/providers/internetx-xml.md).
13+
914
Source code is available on GitHub: [worryboy/internetx-dyndns](https://github.com/worryboy/internetx-dyndns)
1015
Container image is available on Docker Hub: [worryboy/internetx-dyndns](https://hub.docker.com/r/worryboy/internetx-dyndns)
1116

@@ -29,6 +34,8 @@ This project is container-only. The image only needs outbound HTTPS access to:
2934

3035
No inbound ports are required. The worker does not listen on any port, and no `ports:` mapping is needed.
3136

37+
Future providers or future interfaces of the same provider can be added behind the provider layer without changing the container runtime model.
38+
3239
## Image Tags
3340

3441
- `worryboy/internetx-dyndns:0.4.0` - versioned release
@@ -305,6 +312,8 @@ Example and documentation release:
305312
- dedicated Traefik/CrowdSec DynDNS example
306313
- DNS-specific `.env.dns` separation for the example
307314
- reference alignment with the goNeuland Traefik/CrowdSec guide
315+
- provider/interface separation with InterNetX XML as the current implementation
316+
- provider documentation for current InterNetX XML configuration and limits
308317
- `PUSHOVER_LOCATION_PREFIX` examples for IP change notifications
309318

310319
### 0.3.0

README.md

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22

33
`InterNetX DynDNS` is a containerized PHP worker that keeps one explicitly configured DNS host in sync with the current public IP address of the machine or network running the container.
44

5-
The worker uses the InterNetX/AutoDNS XML gateway. The default live XML API endpoint remains `https://gateway.autodns.com`, which is current provider API terminology. Runtime API calls use the documented XML `auth_session` flow: create a session, reuse its hash for this run, then close it.
5+
Current provider: InterNetX / AutoDNS / SchlundTech-related DNS.
6+
Current interface: InterNetX XML.
7+
8+
The default live XML API endpoint remains `https://gateway.autodns.com`. Runtime API calls use the XML `auth_session` flow: create a session, reuse its hash for this run, then close it.
9+
10+
The generic worker flow is separated from the DNS provider/interface implementation. Provider-specific details for the current InterNetX XML support are documented in [docs/providers/internetx-xml.md](docs/providers/internetx-xml.md). Future providers, or a future InterNetX interface such as JSON, can be added behind the provider layer without rewriting the worker core.
611

712
Source code is available on GitHub: [worryboy/internetx-dyndns](https://github.com/worryboy/internetx-dyndns)
813
Container image is available on Docker Hub: [worryboy/internetx-dyndns](https://hub.docker.com/r/worryboy/internetx-dyndns)
@@ -188,9 +193,10 @@ Before running live updates, check the zone for every configured target:
188193
| `HTTP_REQUEST_TIMEOUT` | optional runtime/debug | No | Total HTTP request timeout for IP and XML requests. | `20` |
189194
| `LOG_TARGET` | optional runtime/debug | No | PHP stream or file path for logs. | `php://stdout` |
190195

191-
`INTERNETX_SYSTEM_NS` is not part of XML authentication and is not required for the normal update path. InterNetX documents `system_ns` on the Zone object as the first system-managed nameserver, and ZoneInfo examples may include it. If set, this worker sends it as `<system_ns>` in the read-only ZoneInfo request. Normal DynDNS users usually leave it unset.
196+
`INTERNETX_*` settings are provider-specific for the current InterNetX XML interface. `INTERNETX_SYSTEM_NS` is not part of XML authentication and is not required for the normal update path. InterNetX documents `system_ns` on the Zone object as the first system-managed nameserver, and ZoneInfo examples may include it. If set, this worker sends it as `<system_ns>` in the read-only ZoneInfo request. Normal DynDNS users usually leave it unset.
192197

193198
There are no optional advanced authentication settings in the current worker. Credentials are used for `AuthSessionCreate` and `AuthSessionDelete`; zone inquiry and update requests use `<auth_session><hash>...</hash></auth_session>`.
199+
See [docs/providers/internetx-xml.md](docs/providers/internetx-xml.md) for the provider-specific InterNetX XML configuration notes.
194200

195201
## Safe Local Validation
196202

@@ -290,7 +296,7 @@ Example dry-run startup:
290296
[2026-04-23T08:00:02+00:00] INFO IPv6 detection disabled by configuration attempted=false
291297
[2026-04-23T08:00:02+00:00] SUCCESS Public IP detection completed with at least one usable address ipv4_detected=true ipv6_detected=false
292298
[2026-04-23T08:00:03+00:00] DEBUG Runtime stage entered stage=authentication/session preflight dry_run=true mutation_allowed=false live_mutation_attempted=false
293-
[2026-04-23T08:00:03+00:00] INFO Starting InterNetX authentication/session preflight auth_flow=auth_session session_create_task_code=1321001 mutation_allowed=false
299+
[2026-04-23T08:00:03+00:00] INFO Starting DNS provider authentication/session preflight provider=InterNetX provider_interface=XML auth_flow=auth_session mutation_allowed=false
294300
[2026-04-23T08:00:03+00:00] DEBUG InterNetX XML request prepared operation=AuthSessionCreate task_code=1321001 api_call_type=auth_session_create stage=authentication/session preflight mutation=false dry_run=true auth_mode=session_create session_established=false payload=<request>...</request>
295301
[2026-04-23T08:00:03+00:00] DEBUG InterNetX XML response received operation=AuthSessionCreate task_code=1321001 api_call_type=auth_session_create stage=authentication/session preflight mutation=false dry_run=true auth_mode=session_create session_established=false http_status=200 transport_success=true response_result_status_code=S1321001 response_result_status_type=success stid=20260423-app1 api_business_success=true payload=<response>...</response>
296302
[2026-04-23T08:00:03+00:00] SUCCESS InterNetX session created auth_mode=auth_session session_hash=9b4b...73dd session_persisted=false
@@ -325,6 +331,10 @@ The session hash is treated as a secret. It is kept only in memory, never persis
325331

326332
The updater does not create missing DNS records. If IPv4 is enabled and detected, the target `A` record must already exist. If IPv6 is enabled and detected, the target `AAAA` record must already exist too.
327333

334+
## Verification Direction
335+
336+
The current primary check is provider-side ZoneInfo before update decisions. A future verification pass should stay staged and optional: provider check first, configurable public resolver checks second, and local resolver checks only as a convenience. See [docs/design/verification.md](docs/design/verification.md).
337+
328338
## Docker-Compatible Runtime
329339

330340
Build and run continuously:
@@ -385,10 +395,14 @@ docker compose version
385395
- [`VERSION`](VERSION)
386396
- [`CHANGELOG.md`](CHANGELOG.md)
387397
- [`README.Docker.md`](README.Docker.md)
388-
- [`src/Config.php`](src/Config.php)
389-
- [`src/XmlGatewayClient.php`](src/XmlGatewayClient.php)
390-
- [`src/PublicIpResolver.php`](src/PublicIpResolver.php)
391-
- [`src/DynDnsService.php`](src/DynDnsService.php)
398+
- [`src/Core/DynDnsService.php`](src/Core/DynDnsService.php)
399+
- [`src/Core/PublicIpResolver.php`](src/Core/PublicIpResolver.php)
400+
- [`src/Config/Config.php`](src/Config/Config.php)
401+
- [`src/Provider/DnsProvider.php`](src/Provider/DnsProvider.php)
402+
- [`src/Provider/InterNetX/InterNetXXmlProvider.php`](src/Provider/InterNetX/InterNetXXmlProvider.php)
403+
- [`src/Provider/InterNetX/InterNetXXmlGatewayClient.php`](src/Provider/InterNetX/InterNetXXmlGatewayClient.php)
404+
- [`docs/providers/internetx-xml.md`](docs/providers/internetx-xml.md)
405+
- [`docs/design/verification.md`](docs/design/verification.md)
392406
- [`.env.example`](.env.example)
393407
- [`docker-compose.yml`](docker-compose.yml)
394408
- [`Dockerfile`](Dockerfile)

bin/dyndns.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
$config->connectTimeout(),
3838
$config->requestTimeout()
3939
);
40-
$gateway = new XmlGatewayClient($config, $logger);
40+
$provider = new InterNetXXmlProvider(new InterNetXXmlGatewayClient($config, $logger));
4141
$pushover = new PushoverNotifier(
4242
$logger,
4343
$config->pushoverAppKey(),
@@ -46,7 +46,7 @@
4646
$config->connectTimeout(),
4747
$config->requestTimeout()
4848
);
49-
$service = new DynDnsService($config, $logger, $stateStore, $resolver, $gateway, $pushover);
49+
$service = new DynDnsService($config, $logger, $stateStore, $resolver, $provider, $pushover);
5050

5151
$exitCode = $service->runOnce();
5252
exit($exitCode);

docs/design/verification.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Verification Notes
2+
3+
Provider-side validation is the primary check for this worker. The current InterNetX XML implementation reads the provider zone with ZoneInfo before deciding whether a live update is needed.
4+
5+
A later verification feature should stay lightweight and staged:
6+
7+
1. Provider check: read the authoritative provider state after update when the provider interface supports it.
8+
2. Optional public resolver check: query configured public resolvers to see whether the new value is visible outside the provider.
9+
3. Optional local resolver check: query the local resolver only as an operator convenience.
10+
11+
Local DNS lookup alone is not enough to prove propagation or provider-side correctness. Public resolver checks should be configurable because TTLs, resolver caches, and split DNS can make immediate results noisy.

docs/providers/internetx-xml.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# InterNetX XML Provider
2+
3+
Current provider: InterNetX / AutoDNS / SchlundTech-related DNS service.
4+
Current interface: XML.
5+
6+
The worker uses the XML `auth_session` flow:
7+
8+
- create a session with username, password, and context
9+
- use the session hash for read-only ZoneInfo and live ZoneUpdate requests
10+
- close the session at the end of the cycle
11+
12+
## Required Env
13+
14+
```env
15+
INTERNETX_HOST=https://gateway.autodns.com
16+
INTERNETX_USER=your-api-user
17+
INTERNETX_PASSWORD=your-api-password
18+
INTERNETX_CONTEXT=9
19+
```
20+
21+
Target selection still belongs to the generic worker config:
22+
23+
```env
24+
TARGET_HOST=dyndns.example.com
25+
```
26+
27+
or:
28+
29+
```env
30+
TARGET_HOSTS=app1.example.com,app2.example.com
31+
```
32+
33+
## Optional Provider Env
34+
35+
```env
36+
INTERNETX_SYSTEM_NS=ns.example.com
37+
```
38+
39+
`INTERNETX_SYSTEM_NS` maps to the XML Zone object field `<system_ns>` during read-only zone inquiry. Most DynDNS runs leave it unset.
40+
41+
## Context Handling
42+
43+
`INTERNETX_CONTEXT` is required for the current XML session login. This project defaults the example value to `9`, matching the existing working setup. If your InterNetX/AutoDNS account uses a different context, set it explicitly.
44+
45+
## Assumptions And Limits
46+
47+
- The worker updates existing `A` and `AAAA` records; it does not create missing records.
48+
- The XML templates `request-get.xml` and `request-put.xml` are still used for ZoneInfo and ZoneUpdate.
49+
- `DRY_RUN=true` permits read-only provider validation but blocks live mutation.
50+
- The session hash is kept only in memory and is redacted in debug logs.
51+
- Future InterNetX interfaces, for example JSON, should be added as a separate provider interface implementation rather than changing the XML client in place.

src/Config.php renamed to src/Config/Config.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ public function checkInterval(): int
328328
return $this->checkInterval;
329329
}
330330

331-
public function validateForGateway(): void
331+
public function validateProviderConfig(): void
332332
{
333333
$required = array(
334334
'INTERNETX_HOST' => $this->host,

0 commit comments

Comments
 (0)