Webhook-provider for Domeneshop to be used by external-dns
Find a file
2025-12-12 19:06:19 +00:00
.forgejo chore(deps): update actions/checkout action to v6 2025-12-12 01:01:57 +00:00
ci chore: bump application version to v0.8.2 in metadata.yaml and update version constant in domeneshop client 2025-06-30 23:03:03 +02:00
src chore(deps): update renovate config and Go dependencies 2025-12-11 21:24:39 +01:00
.gitignore chore: update .gitignore, enhance Dockerfile for versioning, and improve README with new configuration options 2025-06-28 15:56:27 +02:00
Dockerfile chore(deps): update golang docker tag to v1.25.5 2025-12-12 01:01:56 +00:00
LICENSE chore: update .gitignore, enhance Dockerfile for versioning, and improve README with new configuration options 2025-06-28 15:56:27 +02:00
openapi.json feat: Add Domeneshop OpenAPI specification, update client and provider implementations, and increase server timeouts. 2025-11-30 10:46:58 +01:00
README.md feat: update README to reflect new features and improve deployment instructions; add unit tests for CNAME flattening and apex handling 2025-06-28 18:11:45 +02:00
renovate.json chore(deps): update renovate config and Go dependencies 2025-12-11 21:24:39 +01:00
Taskfile.yml chore: dual-stack support IP-binding and git-sv -> git 2025-11-04 21:15:52 +01:00

Domeneshop Webhook for ExternalDNS

A lightweight webhook that integrates Domeneshop DNS with Kubernetes ExternalDNS.

🚀 Key Features

  • Automatic DNS record management for Kubernetes services and gateways.
  • CNAME Flattening: Automatically handles CNAME records at the zone apex to avoid conflicts.
  • Supports A, AAAA, CNAME, and TXT records.
  • Simple deployment as a sidecar container.
  • Health and readiness probes.
  • Configurable TLS and CORS support.

📋 Quick Start

Prerequisites

  • A Kubernetes cluster with ExternalDNS installed.
  • Domeneshop API credentials (get yours here).

Deployment

  1. Create a Kubernetes secret for your Domeneshop credentials:

    kubectl create secret generic external-dns-domeneshop-webhook \
      --from-literal=DOMENESHOP_API_TOKEN=<your_api_token> \
      --from-literal=DOMENESHOP_API_SECRET=<your_api_secret>
    
  2. Configure ExternalDNS to use the webhook. Below is a sample values.yaml for the ExternalDNS Helm chart.

    Note: Your extraArgs may vary depending on your specific setup (e.g., if you use Ingress instead of Gateway API).

    # external-dns-domeneshop-values.yaml
    fullnameOverride: external-dns-domeneshop
    
    # Add arguments for your environment. The example below is for Gateway API.
    extraArgs:
    - --gateway-label-filter=app.kubernetes.io/name in (gateway-external)
    - --ignore-hostname-annotation
    - --fqdn-template=external.example.com
    
    # Configure the webhook provider
    provider:
      name: webhook
      webhook:
        image:
          repository: code.252.no/pub/external-dns-domeneshop-webhook
          tag: v0.7.2 # Use the latest version
        env:
        - name: PORT
          value: "8080"
        - name: TOKEN
          valueFrom:
            secretKeyRef:
              name: external-dns-domeneshop-webhook
              key: DOMENESHOP_API_TOKEN
        - name: SECRET
          valueFrom:
            secretKeyRef:
              name: external-dns-domeneshop-webhook
              key: DOMENESHOP_API_SECRET
        # Optional: Set log level to 'debug' for more verbose output
        # - name: LOG_LEVEL
        #   value: debug
        ports:
        - containerPort: 8080
          name: http
        livenessProbe:
          httpGet:
            path: /healthz
            port: http
        readinessProbe:
          httpGet:
            path: /readyz
            port: http
    
    # Recommended settings for optimal performance
    triggerLoopOnEvent: true
    policy: sync
    
    # Configure your sources and domain filters
    sources:
    - gateway-httproute
    txtOwnerId: k8s
    txtPrefix: kube.
    domainFilters:
    - "example.com"
    
  3. Install or upgrade ExternalDNS with your values:

    helm upgrade my-release external-dns/external-dns \
      --install \
      -f external-dns-domeneshop-values.yaml
    

⚙️ Configuration

The webhook is configured entirely through environment variables:

Variable Description Default
TOKEN Domeneshop API token Required
SECRET Domeneshop API secret Required
PORT Server port 8080
TLS_CERT_FILE Path to a TLS certificate file ""
TLS_KEY_FILE Path to a TLS key file ""
ALLOWED_ORIGINS CORS allowed origins (comma-separated) *
LOG_LEVEL Log level (debug, info, warn, error) info

🔍 Implementation Details

Architecture

┌─────────────┐     ┌────────────────┐     ┌──────────────────┐
│ ExternalDNS │────▶│ Webhook Server │────▶│ Domeneshop API   │
└─────────────┘     └────────────────┘     └──────────────────┘

CNAME Flattening (Zone Apex)

Domeneshop, in compliance with DNS RFCs, does not allow a CNAME record at the zone apex (e.g., example.com) if other records (like NS or SOA) exist. Attempting to create one will result in a 409 Conflict error from the API.

To solve this, the webhook automatically performs CNAME flattening. When ExternalDNS requests a CNAME for a zone apex, the webhook:

  1. Resolves the CNAME target to its underlying IP address(es).
  2. Creates A (for IPv4) and/or AAAA (for IPv6) records instead.

This allows you to point your root domain to another service without violating DNS rules.

🤝 Contributing

Contributions are welcome! Please feel free to open an issue or submit a merge request.

📜 License

This project is licensed under the AGPL-3.0-only license. See the LICENSE file for details.

🤖 Dependency Management

This project uses Renovate to keep dependencies up-to-date. See renovate.json for the configuration.

🧪 Development

To run the webhook locally for development:

  1. Set environment variables with your Domeneshop API credentials:

    export DOMENESHOP_API_TOKEN="your_api_token"
    export DOMENESHOP_API_SECRET="your_api_secret"
    
  2. Run the webhook:

    go run ./src/main.go
    

    The server will start on http://localhost:8080.

  3. Test with ExternalDNS:

    You can configure a local ExternalDNS instance to point to your development server. To expose your local server to your cluster, a tool like ngrok can be used.

    Update your external-dns-domeneshop-values.yaml with the public URL from ngrok:

    # ...
    provider:
      webhook:
        url: "https://your-ngrok-url.ngrok.io"
    # ...
    

    Then apply the changes:

    helm upgrade my-release external-dns/external-dns \
      -f external-dns-domeneshop-values.yaml
    

📚 Resources