ExternalDNS webhook to manage OPNsense Unbound DNS records
APACHE-2.0 License
This webhook graciously stolen 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 Unbound service.
As of this writing this webhook only supports creating A records using Unbound's Host Overrides. Theoretically AAAA records should work without much (if any) modification. A/AAAA records work because they effectively map 1:1 with Host Overrides. With significantly more effort, CNAMEs could be supported and mapped to Host Override Aliases, which I may or may not implement.
Furthermore, due to lack of support for TXT records in OPNsense's Unbound API we cannot leverage external-dns' normal registry
behavior. Using an external registry would be optimal but not required for this webhook to function. Without a valid registry there is no concept of DNS record "ownership". This means that the webhook will assume ownership of all Host Overrides that match domainFilters
in Unbound. There is a DNS pattern that exists to overcome this limitation detailed below.
[!WARNING] If you don't follow this manually entered A/AAAA records can be permanently destroyed
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 structure them in a way that avoids conflict. The webhook examines all records defined in Host Overrides but does not evaluate any Aliases. To avoid ownership conflicts you should create a "stub" Host Override pointing to your intended IP address, then create aliases that use your desired domain.
For example:
example.com
host1.example.com
in OPNSense's Unbound Web UI pointed to 192.168.10.2
.You will need to:
host1.fake.com
pointing to 192.168.10.2
host1.fake.com
that uses the original domain like host1.example.com
This effecively protects your record from ownership conflicts while still allowing you to define custom records for a domain used by this webhook. Another option would be to create dnsendpoint
CRDs for all the records you need in Unbound and let the webhook manage everything.
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: Unbound DNS: Edit Host and Domain Override
Services: Unbound (MVC)
Status: DNS Overview
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-secret
that holds api_key
and api_secret
with 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/crutonjohn/external-dns-opnsense-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-wh-metrics
initialDelaySeconds: 10
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /readyz
port: http-wh-metrics
initialDelaySeconds: 10
timeoutSeconds: 5
extraArgs:
- --ignore-ingress-tls-spec
policy: sync
sources: ["ingress", "service", "crd"]
registry: noop
domainFilters: ["example.com"] # replace with your domain
Install the Helm chart
helm install external-dns-opnsense external-dns/external-dns -f external-dns-opnsense-values yaml --version 1.14.3 -n external-dns
Thanks to all the people who donate their time to the Home Operations Discord community.
I'd like to thank the following people for answering my hare-brained questions: