Files
dns-sync/README.md
T

3.0 KiB

dns-sync modular

A plugin-based DNS reconciliation service.

Plugin contract

Source modules live under dns_sync/plugins/sources/ and export a class named Plugin with:

  • name
  • discover() -> list[DesiredRecord]

Provider modules live under dns_sync/plugins/providers/ and export Plugin with:

  • name
  • capabilities
  • connect()
  • list_records()
  • add_record()
  • update_record()
  • delete_record()
  • verify_record()
  • capacity()

Plugins are convention-loaded from environment variables. No registry edit is needed:

  • PROXY=npm loads dns_sync.plugins.sources.npm.Plugin
  • DNS_PROVIDER=verizon loads dns_sync.plugins.providers.verizon.Plugin

Plugins

The generic reconciliation engine uses adapter plugins.

Built-in adapters:

  • npm - Nginx Proxy Manager proxy hosts and certificates

External adapters are loaded from DNS_SYNC_PLUGIN_PATH and must also be explicitly enabled through ENABLED_PLUGINS.

Example:

ENABLED_PLUGINS=npm,verizon
DNS_SYNC_PLUGIN_PATH=/plugins

The Verizon adapter lives under external_plugins/verizon and is deliberately not copied into the dns-sync container image. The external plugin directory is mounted read-only at runtime.

Container runtime

The container periodically executes the generic reconciliation engine:

python -m dns_sync reconcile

The default container mode is read-only.

SYNC_INTERVAL=1
DNS_SYNC_APPLY=false
DNS_SYNC_STATE_DB=/var/lib/dns-sync/state.sqlite3
DNS_SYNC_PLUGIN_PATH=/plugins
ENABLED_PLUGINS=npm,verizon

SYNC_INTERVAL is expressed in minutes. The interval begins after each reconciliation completes, so runs do not overlap.

To permit planned changes to be applied and verified:

DNS_SYNC_APPLY=true

An optional mutation budget can be specified:

DNS_SYNC_MAX_CHANGES=10

When DNS_SYNC_APPLY=false, reconciliation still performs discovery, normalization, desired-state reconciliation, planning, and state persistence, but no endpoint mutations are executed.

The SQLite database should be stored on persistent storage.

External plugins should be mounted read-only at /plugins.

Main environment

NPM_ENDPOINTS=192.168.1.3,192.168.1.4,192.168.1.5
PROXY_API_PORT=81
PROXY_API_SCHEME=http
DNS_TARGET_IP=192.168.1.101

VERIZON_ENDPOINTS=192.168.1.1

DOMAIN_MATCHING=false
DOMAIN=example.com

NPM_IDENTITY=...
NPM_SECRET=...
ROUTER_PASSWORD=...

Domain filtering

Filtering is performed by the central engine after source discovery and before reconciliation, so all source plugins get identical behavior.

DOMAIN_MATCHING=true
DOMAIN=example.com

This includes the apex and every subdomain:

example.com
app.example.com
deep.app.example.com
*.example.com

It excludes unrelated names such as:

example.net
notexample.com
example.com.evil.net

When DOMAIN_MATCHING=false, DOMAIN is ignored and all discovered records are eligible for reconciliation.

When DOMAIN_MATCHING=true, DOMAIN is required.