A webhook provider for external-dns that manages DNS records on Firewalla devices via dnsmasq configuration files.
This project enables external-dns to automatically manage DNS records on your Firewalla device for services running in Kubernetes. It consists of two components:
- Webhook Provider: A Node.js service that runs directly on your Firewalla device
- Webhook Proxy: A lightweight HTTP proxy that runs as a sidecar container in your external-dns Kubernetes deployment
flowchart LR
subgraph K ["Kubernetes Cluster"]
direction LR
A["Service/Ingress<br/>with DNS annotations"] --> B["External-DNS<br/>Controller"]
B --> C["Webhook Proxy<br/>Sidecar Container"]
end
subgraph F ["Firewalla Device"]
direction LR
D["Webhook Provider<br/>Node.js Server"] --> E["dnsmasq Config Files<br/>~/.firewalla/config/dnsmasq_local/"]
E --> L["firerouter_dns<br/>Service"]
L --> G["DNS Resolution<br/>Network-wide"]
end
C -->|"HTTP Proxy"| D
- Firewalla device (Gold, Purple, Red, or any model with SSH access)
- Firewalla firmware with Node.js at
/home/pi/firewalla/bin/node - Kubernetes cluster with external-dns installed
- SSH access to your Firewalla device as the
piuser - Sudo privileges on Firewalla
Each steps needs to be ran on either your firewalla device or your Kubernetes cluster. The headers tell you where you should run it.
+ Security Notice
+ This project uses JWT authentication with a shared secret between the webhook proxy
+ and the Firewalla provider. Ensure your SHARED_SECRET is strong and kept secure.
+ Only expose the webhook proxy to trusted networks within your Kubernetes cluster.SSH into your Firewalla as the pi user and run:
# Set your domain filter
echo "home.local,*.home.local" > /tmp/external-dns-domain-filter
# Generate and set a secure shared secret (same secret must be used in Kubernetes)
openssl rand -hex 32 > /tmp/external-dns-shared-secret
# Or manually set: echo "your-secure-random-secret-here" > /tmp/external-dns-shared-secretcurl -fsSL https://raw.githubusercontent.com/TheOutdoorProgrammer/external-dns-firewalla-webhook/main/scripts/install.sh | bashUse the example helm values:
# Use webhook provider
provider:
name: webhook
webhook:
image:
repository: ghcr.io/theoutdoorprogrammer/external-dns-firewalla-webhook
tag: 1.1.0
env:
- name: FIREWALLA_HOST
value: "192.168.229.1"
- name: FIREWALLA_PROVIDER_PORT
value: "8888"
- name: FIREWALLA_HEALTH_PORT
value: "8080"
- name: WEBHOOK_PORT
value: "8888"
- name: METRICS_PORT
value: "8080"
- name: SHARED_SECRET # You can use a secret reference, must match Firewalla shared secret (recommended)
valueFrom:
secretKeyRef:
name: firewalla-webhook-secret
key: shared-secret
# - name: SHARED_SECRET # Or manually set the shared secret (not recommended)
# value: "your-secure-shared-secret-here"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 10
timeoutSeconds: 5
# Domain filter - must match Firewalla webhook configuration
domainFilters:
- home.local
# Source configuration - what Kubernetes resources to watch
sources:
- service
- ingress
# Policy settings
policy: sync
registry: txt
txtOwnerId: home-k8s-cluster
# TXT record prefix
extraArgs:
- --txt-prefix=external-dns-
# Logging
logLevel: info
logFormat: text
# Resources
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
# Single replica is sufficient for home lab
replicaCount: 1
# Service account
serviceAccount:
create: true
# RBAC
rbac:
create: trueHeres a quick way to create the above secret if you use the recommended approach:
kubectl create secret generic firewalla-webhook-secret --from-literal=shared-secret="{your-secure-shared-secret-here}"Configure via /opt/external-dns-firewalla-webhook/.env:
DOMAIN_FILTER=home.local,*.home.local
SHARED_SECRET=your-secure-shared-secret-here
PORT_PROVIDER=8888
PORT_HEALTH=8080
DNS_TTL=300
DNSMASQ_DIR=/home/pi/.firewalla/config/dnsmasq_local
LOG_LEVEL=info
DRY_RUN=false
RESTART_COMMAND=sudo systemctl restart firerouter_dnsEnvironment variables for the sidecar container:
FIREWALLA_HOST: IP address of your Firewalla deviceFIREWALLA_PROVIDER_PORT: Provider API port on Firewalla (default: 8888)FIREWALLA_HEALTH_PORT: Health check port on Firewalla (default: 8080)WEBHOOK_PORT: Port for webhook proxy to listen on (default: 8888)METRICS_PORT: Port for health/metrics endpoints (default: 8080)SHARED_SECRET: Shared secret for JWT authentication (must match Firewalla provider)
- A records (IPv4 addresses)
- CNAME records (domain aliases)
- TXT records (for external-dns ownership tracking)
The webhook provider exposes these endpoints:
GET /: Domain filter negotiation with external-dnsGET /records: Retrieve current DNS recordsPOST /records: Apply DNS record changesPOST /adjustendpoints: Filter unsupported record typesGET /healthz: Health check endpoint
DNS records are stored as individual files in /home/pi/.firewalla/config/dnsmasq_local/:
example.home.local
├── address=/example.home.local/192.168.1.100
└── address=/example.home.local/192.168.1.101
api.home.local
└── cname=api.home.local,service.home.local
-
Verify the webhook provider is running on Firewalla:
sudo systemctl status external-dns-firewalla-webhook curl http://localhost:8080/healthz
-
Check external-dns pod status:
kubectl get pods -n external-dns kubectl logs -n external-dns -l app=external-dns
-
Create a test service with DNS annotations:
apiVersion: v1 kind: Service metadata: name: nginx-test annotations: external-dns.alpha.kubernetes.io/hostname: nginx.home.local spec: type: LoadBalancer ports: - port: 80 selector: app: nginx
-
Service won't start on Firewalla
- Check logs:
sudo journalctl -u external-dns-firewalla-webhook -n 50 - Verify Node.js:
/home/pi/firewalla/bin/node -v - Check .env file:
cat /opt/external-dns-firewalla-webhook/.env
- Check logs:
-
DNS records not created
- Verify domain filter matches between Firewalla and external-dns
- Check external-dns logs for connectivity issues
- Test network connectivity from Kubernetes to Firewalla
-
DNS service restart fails
- Check sudo permissions:
sudo -l | grep firerouter_dns - Test manually:
sudo systemctl restart firerouter_dns
- Check sudo permissions:
- Clone the repository
- Install dependencies:
npm install - Copy
.env.exampleto.envand configure - Run in development mode:
npm run dev
MIT