dnsweaver

repository·main·Indexed 41 days ago

https://github.com/maxfield-allison/dnsweaver

An automation tool for managing DNS records for containers, VMs, and clusters. It bridges infrastructure sources—including Docker, Kubernetes, Proxmox, and Incus—with DNS providers such as Cloudflare, Technitium, and Pi-hole to enable automatic record creation, cleanup, and Split-Horizon DNS configurations.

Tokens
80.8K
Snippets
221
Records
326
Agent score
51%

What's inside dnsweaver

  1. Choose a dnsweaver deployment option

    main

    dnsweaver can be deployed in several environments depending on your complexity and availability requirements. Choose the method that best fits your infrastructure:

    • Docker Compose: The simplest deployment for single-host environments. Recommended for getting started and development.
    • Docker Swarm: A production deployment for multi-node Docker clusters requiring high availability.
    • Kubernetes: Deploy via Helm or Kustomize. This mode watches Ingress, IngressRoute, HTTPRoute, and Service resources.
    • Split-Horizon DNS: A specialized configuration to manage internal and external DNS records using the same container labels.
  2. Overview of dnsweaver core concepts

    main

    dnsweaver automates DNS record management by connecting Sources to Providers.

    • Sources: Where hostnames are discovered (e.g., Traefik labels, Kubernetes Ingress, Proxmox VMs, or Incus containers).
    • Engine: The core logic that watches for events from sources, matches hostnames to domain patterns, and reconciles the desired state against the live DNS records.
    • Providers: Where the records are actually written (e.g., Technitium, Cloudflare, Pi-hole).

    By using multiple providers simultaneously, you can implement Split-Horizon DNS, where the same container labels trigger both internal (e.g., Technitium) and external (e.g., Cloudflare) DNS updates.

  3. Architecture Patterns for Split-Horizon DNS

    main

    Depending on your network setup, choose one of the following patterns for your dnsweaver configuration:

    Pattern 1: Cloudflare Tunnel + Internal DNS

    Use this when you want to expose services via a Cloudflare Tunnel externally, but access them directly via a reverse proxy internally.

    • External: CNAME record pointing to your Cloudflare Tunnel address.
    • Internal: A record pointing to your internal reverse proxy IP.

    Pattern 2: Public IP + Internal IP

    Use this when you have a static public IP and want to route external traffic through NAT.

    • External: A record pointing to your public IP.
    • Internal: A record pointing to your private/LAN IP.

    Pattern 3: Different Subdomains

    Use different subdomain schemes for internal vs external routing.

    • Internal: *.home.example.com
    • External: *.example.com (with *.home.example.com excluded via EXCLUDE_DOMAINS).
  4. How record adoption precedence works

    main

    dnsweaver follows a specific hierarchy to determine if an existing record should be 'adopted' (claimed) by the system. The precedence order is:

    1. Named Record: dnsweaver.records.<name>.adopt (most specific)
    2. Workload: dnsweaver.adopt (applies to hostnames found on the workload)
    3. Provider Instance: DNSWEAVER_{NAME}_ADOPT_EXISTING
    4. Global: DNSWEAVER_ADOPT_EXISTING (least specific)

    Security Note: Workload configuration is not trusted by default. An adopt=true label or annotation is ignored unless the matching provider has DNSWEAVER_{NAME}_ADOPT_EXISTING_ALLOW_OVERRIDES=true. However, an adopt=false value is always honored to ensure dnsweaver's authority is restricted as requested.

  5. Configure Traefik file watching methods

    main

    You can choose how dnsweaver detects changes in your Traefik configuration files using the DNSWEAVER_SOURCE_TRAEFIK_WATCH_METHOD variable.

    • auto (Default): Uses inotify if available on the system, otherwise falls back to poll.
    • inotify: Uses Linux filesystem events for near-instant detection of changes.
    • poll: Periodically scans files at the interval defined by DNSWEAVER_SOURCE_TRAEFIK_POLL_INTERVAL. This is the most compatible method across different platforms.
    # Example using polling
    - DNSWEAVER_SOURCE_TRAEFIK_WATCH_METHOD=poll
    - DNSWEAVER_SOURCE_TRAEFIK_POLL_INTERVAL=30s
  6. How dnsweaver works

    main

    dnsweaver automates DNS record management by following a three-step lifecycle:

    1. Watch: It monitors various sources (Docker/Swarm, Kubernetes, Proxmox VE, or Incus) for events.
    2. Match: It extracts hostnames from these sources and matches them against configured provider domain patterns.
    3. Reconcile: It compares the desired state (from the sources) against the live state in the DNS providers, performing create, update, or delete operations to ensure they match.

    This process enables split-horizon DNS, where a single source (like a container label) can trigger updates to both internal (e.g., Technitium, Pi-hole) and external (e.g., Cloudflare, OVH) DNS providers simultaneously.

  7. How secrets management works in dnsweaver

    main

    dnsweaver supports file-backed provider credentials for Docker and Kubernetes using the _FILE suffix pattern. When a setting with a _FILE suffix is configured, dnsweaver reads the file contents at startup and uses them as the variable value.

    Precedence and Behavior:

    • A configured _FILE setting takes precedence over direct environment values and YAML configuration.
    • If the file cannot be read, configuration loading fails; dnsweaver will not fall back to a direct value or an older credential.
    • An empty file is treated as an explicit empty value and is subject to standard validation.
    • Secret File Format: Files must contain only the raw secret value. Do not include variable names, quotes, or other formatting.
    # Instead of exposing the secret in the environment:
    environment:
      - DNSWEAVER_INTERNAL_DNS_TOKEN=my-secret-token  # ❌ Exposed
    
    # Use the _FILE suffix to read from a mounted secret:
    environment:
      - DNSWEAVER_INTERNAL_DNS_TOKEN_FILE=/run/secrets/dns_token  # ✅ Secure
  8. Understand Incus hostname resolution precedence

    main

    The Incus source determines the DNS hostname for each instance using the following order of precedence:

    1. user.dnsweaver.hostname config key: Set directly on the Incus instance (e.g., incus config set web user.dnsweaver.hostname=app.example.net).
    2. dnsweaver.hostname label: The incus-compose form of the override.
    3. Instance name contains a dot: The name is used directly as an FQDN (e.g., db.home.example.com).
    4. Configured domain suffix: Appended to the instance name (e.g., web + home.example.com $\rightarrow$ web.home.example.com).

    Warning: If none of these apply, the instance is skipped. It is strongly recommended to set DNSWEAVER_INCUS_DOMAIN_SUFFIX to ensure all instances are registered.

  9. Use Dynamic Targets for IP resolution

    main

    Instead of a static IP, use TARGET_MODE to resolve the target at runtime. This is useful for dynamic public IPs, VPN addresses, or specific local interfaces.

    Supported Modes

    • public: Discovers the host's public IP via HTTP echo endpoints (e.g., checkip.amazonaws.com, api.ipify.org). This functions as a dynamic-DNS setup.
    • interface:<name>: Resolves to the primary global IP of a local network interface (e.g., interface:eth0).

    Behavior

    • Family-aware: A records resolve IPv4; AAAA records resolve IPv6. CNAME cannot be used with dynamic modes.
    • Fallback: If DNSWEAVER_{NAME}_TARGET is set, it is used as an optional fallback until the first successful resolution.
    • Resilience: If resolution fails, dnsweaver keeps the last known-good value and logs a warning to prevent DNS churn during transient failures.
    • Refresh: Targets are re-resolved every DNSWEAVER_{NAME}_TARGET_REFRESH_INTERVAL (default 5m).

    Note for Containers: interface:<name> refers to the interface visible inside the container's network namespace.

    # Point all matched records at this host's current public IP, refreshed every 5m
    DNSWEAVER_PUBLIC_DNS_TYPE=cloudflare
    DNSWEAVER_PUBLIC_DNS_RECORD_TYPE=A
    DNSWEAVER_PUBLIC_DNS_TARGET_MODE=public
    DNSWEAVER_PUBLIC_DNS_DOMAINS=*.example.com
    
    # Or use a specific interface's IP
    DNSWEAVER_LAN_DNS_TARGET_MODE=interface:eth0
  10. How Pi-hole ownership works in managed mode

    main

    Because Pi-hole does not support TXT records, dnsweaver cannot use standard ownership markers (_dnsweaver.*). Instead, it uses target-based ownership inference in managed mode.

    If a record's RECORD_TYPE and TARGET match the provider instance's configured values, dnsweaver infers ownership. When the source workload disappears, dnsweaver will clean up these records. Records with different targets are preserved and not managed by this instance.

  11. Configure Platform mode (Docker, Kubernetes, or Standalone)

    main

    The DNSWEAVER_PLATFORM variable determines which workload sources are active. Use DNSWEAVER_INSTANCE_ID for unique identification in multi-instance setups.

    • docker (default): Watches Docker containers/services.
    • kubernetes: Watches Kubernetes Ingress, IngressRoute, HTTPRoute, and Service resources.
    • both: Watches both Docker and Kubernetes workloads.
    • none (or standalone): Disables container-runtime clients. Use this when running as a bare binary on a host/VM. You must configure at least one non-container source (e.g., Proxmox, Incus, or a file-discovery source) or startup will fail.
  12. Use per-entrypoint routing for split-horizon DNS

    main

    You can run multiple dnsweaver instances (e.g., lan and vpn) that serve different targets based on the Traefik entrypoint used.

    Use the DNSWEAVER_{NAME}_ENTRYPOINTS environment variable to provide a comma-separated allowlist of entrypoints for a specific instance.

    Matching Logic:

    • A router with an explicit entrypoints label produces one extraction per (host, entrypoint) pair. Each instance only receives the pair if the entrypoint is in its allowlist.
    • A router with no entrypoints label produces a wildcard extraction that matches every instance (pre-1.4 behavior).
    • If using Traefik's asDefault = true setting, you must inform dnsweaver of the default entrypoints using DNSWEAVER_SOURCE_TRAEFIK_DEFAULT_ENTRYPOINTS to prevent over-publishing records.
    services:
      # ... proxy and app services ...
    
      dnsweaver:
        image: maxamill/dnsweaver:latest
        environment:
          - DNSWEAVER_INSTANCES=lan,vpn
          # LAN instance
          - DNSWEAVER_LAN_TYPE=technitium
          - DNSWEAVER_LAN_TARGET=10.0.0.10
          - DNSWEAVER_LAN_DOMAINS=*.example.com
          - DNSWEAVER_LAN_ENTRYPOINTS=webA
          # VPN instance
          - DNSWEAVER_VPN_TYPE=technitium
          - DNSWEVER_VPN_TARGET=10.99.0.10
          - DNSWEAVER_VPN_DOMAINS=*.example.com
          - DNSWEAVER_VPN_ENTRYPOINTS=webB