This webhook graciously stolen from inspired by crutonjohn's OPNSense Unbound Webhook (which in turn was inspired by Kashall's Unifi Webhook).
Warning
This software is experimental and NOT FIT FOR PRODUCTION USE!
ExternalDNS is a Kubernetes add-on for automatically managing DNS records for Kubernetes ingresses and services by using different DNS providers. This webhook provider allows you to automate DNS records from your Kubernetes clusters into your OPNsense Firewall's os-bind plugin.
As of this writing this webhook supports A, AAAA, TXT, and absolute FQDN CNAME records using the BIND plugin's API.
Usage of a registry is supported, though only tested with TXT type registry.
- As mentioned above, with CNAME records this only works when the target of our record is an absolute FQDN. Relative targets like "beep" or "beep.boop" will not work. Future work may make this possible, but right now I haven't figured out how to determine what to do with a relative target and how to handle it.
- Wildcard CNAME records are not supported at this time. Attempting to use this provider to create them will result in the ExternalDNS process crashing. I am very welcome to PRs to figure this out.
Warning
If you don't follow this, manually entered A/AAAA/CNAME records can be permanently destroyed
Note
This only applies if you are using registry=noop with policy=sync. If you plan to use the TXT registry along with policy=sync, then this warning should not apply to you as records will be skipped if they are missing the required TXT ownership records.
If you have records that are managed manually or by some process other than this webhook and you intend for those records to share a domain, then you must use policy=upsert-only or policy=create-only with your ExternalDNS deployment. If you use policy=sync, this will attempt to reconcile the zone by deleting all A/AAAA/CNAME records not currently defined by a supported ExternalDNS source in-cluster.
Warning
It is highly recommended to set a unique txtOwnerId for each ExternalDNS deployment, including this one. Not doing so will result in Bad Situations™ if you attempt to use multiple ExternalDNS providers with the same domains.
The TXT registry is supported with this provider. There is special instructions if you would like to use this provider to manage the apex records of a domain (ie the @ record for example.com in the example.com domain). If you would like to do this and use the TXT registry, it is required that you use a txtPrefix value that ends in a singular period character .. Example: reg-%{record_type}-externaldns.
Failing to do this will result in this provider crashing ExternalDNS if you attempt to create TXT registry entries for apex records due to it attempting to create TXT records like a-example.com rather than a.example.com.
-
Create a local user with a password in your OPNsense firewall.
System > Access > Users -
Create an API keypair for the user you created in step 1.
-
Create (or use an existing) group to limit your user's permissions. The known required privileges are:
Services: BINDStatus: Services
-
Add the ExternalDNS Helm repository to your cluster.
helm repo add external-dns https://kubernetes-sigs.github.io/external-dns/
-
Create a Kubernetes secret called
external-dns-opnsense-secretthat holdsapi_keyandapi_secretwith their respective values from step 1:apiVersion: v1 stringData: api_secret: <INSERT API SECRET> api_key: <INSERT API KEY> kind: Secret metadata: name: external-dns-opnsense-secret type: Opaque
-
Create the helm values file, for example
external-dns-webhook-values.yaml:fullnameOverride: external-dns-opnsense logLevel: debug provider: name: webhook webhook: image: repository: ghcr.io/KittyKatt/external-dns-opnsense-bind-webhook tag: main # replace with a versioned release tag env: - name: OPNSENSE_API_SECRET valueFrom: secretKeyRef: name: external-dns-opnsense-secret key: api_secret - name: OPNSENSE_API_KEY valueFrom: secretKeyRef: name: external-dns-opnsense-secret key: api_key - name: OPNSENSE_HOST value: https://192.168.1.1 # replace with the address to your OPNsense router - name: OPNSENSE_SKIP_TLS_VERIFY value: "true" # optional depending on your environment - name: LOG_LEVEL value: debug livenessProbe: httpGet: path: /healthz port: http-webhook` initialDelaySeconds: 10 timeoutSeconds: 5 readinessProbe: httpGet: path: /readyz port: http-webhook` initialDelaySeconds: 10 timeoutSeconds: 5 extraArgs: - --ignore-ingress-tls-spec policy: sync # can be upsert-only, create-only, or sync sources: ["ingress", "service", "crd"] registry: txt # can be noop or txt domainFilters: ["example.com"] # replace with your domain txtPrefix: reg-%{record_type}-externaldns. txtOwnerId: opnsense-bind
-
Install the Helm chart
helm install external-dns-opnsense external-dns/external-dns -f external-dns-opnsense-values yaml -n external-dns
Build:
go build -ldflags "-s -w -X main.Version=test -X main.Gitsha=test" ./cmd/webhookRun:
OPNSENSE_HOST=https://192.168.0.1 OPNSENSE_API_SECRET=<secret value> OPNSENSE_API_KEY=<key value> ./webhookThank you to @crutonjohn @kashalls for their wonderful work that allowed me to create this and get it working with minimal effort.