BIG-IP Next for Kubernetes CNFs - DNS walkthrough
Table of Contents
Introduction
F5 enables advanced DNS implementations across different deployments, whether it’s hardware, Virtual Functions and F5 Distributed Cloud. Also, in Kubernetes environment through the F5BigDnsApp Custom Resource Definition (CRD), allowing declarative configuration of DNS listeners, pools, monitors, and profiles directly in-cluster.
Deploying DNS services like Express, Cache, and DoH within the Kubernetes cluster using BIG-IP Next for Kubernetes CNF DNS saves external traffic by resolving queries locally (reducing egress to upstream resolvers by up to 80% with caching) and enhances security through in-cluster isolation, mTLS enforcement, and protocol encryption like DoH, preventing plaintext DNS exposure over cluster boundaries.
This article provides a walkthrough for DNS Express, DNS Cache, and DNS-over-HTTPS (DoH) on top of Red Hat OpenShift.
Prerequisites
- Deploy BIG-IP Next for Kubernetes CNF following the steps in F5’s Cloud-Native Network Functions (CNFs)
- Verify the nodes and CNF components are installed
[cloud-user@ocp-provisioner f5-cne-2.1.0]$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
master-1.ocp.f5-udf.com Ready control-plane,master,worker 2y221d v1.29.8+f10c92d
master-2.ocp.f5-udf.com Ready control-plane,master,worker 2y221d v1.29.8+f10c92d
master-3.ocp.f5-udf.com Ready control-plane,master,worker 2y221d v1.29.8+f10c92d
worker-1.ocp.f5-udf.com Ready worker 2y221d v1.29.8+f10c92d
worker-2.ocp.f5-udf.com Ready worker 2y221d v1.29.8+f10c92d
[cloud-user@ocp-provisioner f5-cne-2.1.0]$ kubectl get pods -n cne-core
NAME READY STATUS RESTARTS AGE
f5-cert-manager-656b6db84f-dmv78 2/2 Running 10 (15h ago) 19d
f5-cert-manager-cainjector-5cd9454d6c-sc8q2 1/1 Running 21 (15h ago) 19d
f5-cert-manager-webhook-6d87b5797b-954v6 1/1 Running 4 19d
f5-dssm-db-0 3/3 Running 13 (18h ago) 15d
f5-dssm-db-1 3/3 Running 0 18h
f5-dssm-db-2 3/3 Running 4 (18h ago) 42h
f5-dssm-sentinel-0 3/3 Running 0 14h
f5-dssm-sentinel-1 3/3 Running 10 (18h ago) 5d8h
f5-dssm-sentinel-2 3/3 Running 0 18h
f5-rabbit-64c984d4c6-xn2z4 2/2 Running 8 19d
f5-spk-cwc-77d487f955-j5pp4 2/2 Running 9 19d
[cloud-user@ocp-provisioner f5-cne-2.1.0]$ kubectl get pods -n cnf-fw-01
NAME READY STATUS RESTARTS AGE
f5-afm-76c7d76fff-5gdhx 2/2 Running 2 42h
f5-downloader-657b7fc749-vxm8l 2/2 Running 0 26h
f5-dwbld-d858c485b-6xfq8 2/2 Running 2 26h
f5-ipsd-79f97fdb9c-zfqxk 2/2 Running 2 26h
f5-tmm-6f799f8f49-lfhnd 5/5 Running 0 18h
f5-zxfrd-d9db549c4-6r4wz 2/2 Running 2 (18h ago) 26h
f5ingress-f5ingress-7bcc94b9c8-zhldm 5/5 Running 6 26h
otel-collector-75cd944bcc-xnwth 1/1 Running 1 42h
DNS Express Walkthrough
DNS Express configures BIG-IP to authoritatively answer queries for a zone by pulling it via AXFR/IXFR from an upstream server, with optional TSIG auth keeping zone data in-cluster for low-latency authoritative resolution.
Step 1: Create a F5BigDnsZone CR for zone transfer (e.g., example.com from upstream 10.1.1.12).
# cat 10-cr-dnsxzone.yaml
apiVersion: k8s.f5net.com/v1
kind: F5BigDnsZone
metadata:
name: example.com
spec:
dnsxAllowNotifyFrom: ["10.1.1.12"]
dnsxServer:
address: "10.1.1.12"
port: 53
dnsxEnabled: true
dnsxNotifyAction: consume
dnsxVerifyNotifyTsig: false
#kubectl apply -f 10-cr-dnsxzone.yaml -n cnf-fw-01
Step 2: Deploy F5BigDnsApp CR with DNS Express enabled
# cat 11-cr-dnsx-app-udp.yaml
apiVersion: "k8s.f5net.com/v1"
kind: F5BigDnsApp
metadata:
name: "dnsx-app-listener"
namespace: "cnf-fw-01"
spec:
destination:
address: "10.1.30.100"
port: 53
ipProtocol: "udp"
snat:
type: "automap"
dns:
dnsExpressEnabled: true
logProfile: "cnf-log-profile"
# kubectl apply -f 11-cr-dnsx-app-udp.yaml -n cnf-fw-01
Step 3: Validate: Query from our client pod & tmm statistics
dig @10.1.30.100 www.example.com
; <<>> DiG 9.18.30-0ubuntu0.20.04.2-Ubuntu <<>> @10.1.30.100 www.example.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 43865
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 2
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.example.com. IN A
;; ANSWER SECTION:
www.example.com. 604800 IN A 192.168.1.11
;; AUTHORITY SECTION:
example.com. 604800 IN NS ns.example.com.
;; ADDITIONAL SECTION:
ns.example.com. 604800 IN A 192.168.1.10
;; Query time: 0 msec
;; SERVER: 10.1.30.100#53(10.1.30.100) (UDP)
;; WHEN: Thu Jan 22 11:10:24 UTC 2026
;; MSG SIZE rcvd: 93
kubectl exec -it deploy/f5-tmm -c debug -n cnf-fw-01 -- bash
/tmctl -id blade tmmdns_zone_stat name=example.com
name dnsx_queries dnsx_responses dnsx_xfr_msgs dnsx_notifies_recv
----------- ------------ -------------- ------------- ------------------
example.com 2 2 0 0
DNS Cache Walkthrough
DNS Cache reduces latency by storing responses non-authoritatively, referenced via a separate cache CR in the DNS profile, cutting repeated upstream queries and external bandwidth use.
Step 1: Create a DNS Cache CR F5BigDnsCache
# cat 13-cr-dnscache.yaml
apiVersion: "k8s.f5net.com/v1"
kind: F5BigDnsCache
metadata:
name: "cnf-dnscache"
spec:
cacheType: resolver
resolver:
useIpv4: true
useTcp: false
useIpv6: false
forwardZones:
- forwardZone: "example.com"
nameServers:
- ipAddress: 10.1.1.12
port: 53
- forwardZone: "."
nameServers:
- ipAddress: 8.8.8.8
port: 53
# kubectl apply -f 13-cr-dnscache.yaml -n cnf-fw-01
Step 2: Deploy F5BigDnsApp CR with DNS Cache enabled
# cat 11-cr-dnsx-app-udp.yaml
apiVersion: "k8s.f5net.com/v1"
kind: F5BigDnsApp
metadata:
name: "dnsx-app-listener"
namespace: "cnf-fw-01"
spec:
destination:
address: "10.1.30.100"
port: 53
ipProtocol: "udp"
snat:
type: "automap"
dns:
dnsCache: "cnf-dnscache"
logProfile: "cnf-log-profile"
# kubectl apply -f 11-cr-dnsx-app-udp.yaml -n cnf-fw-01
Step 3: Validate: Query from our client pod
dig @10.1.30.100 www.example.com
; <<>> DiG 9.18.30-0ubuntu0.20.04.2-Ubuntu <<>> @10.1.30.100 www.example.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 18302
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.example.com. IN A
;; ANSWER SECTION:
www.example.com. 19076 IN A 192.168.1.11
;; Query time: 4 msec
;; SERVER: 10.1.30.100#53(10.1.30.100) (UDP)
;; WHEN: Thu Jan 22 11:04:45 UTC 2026
;; MSG SIZE rcvd: 60
DoH Walkthrough
DoH exposes DNS over HTTPS (port 443) for encrypted queries, using BIG-IP's protocol inspection and UDP profiles, securing in-cluster DNS from eavesdropping and MITM attacks.
Step 1: Ensure TLS secret exists and HTTP profiles exist
# cat 14-tls-clientsslsettings.yaml
apiVersion: k8s.f5net.com/v1
kind: F5BigClientsslSetting
metadata:
name: "cnf-clientssl-profile"
namespace: "cnf-fw-01"
spec:
enableTls13: true
enableRenegotiation: false
renegotiationMode: "require"
# cat 15-http-profiles.yaml
apiVersion: "k8s.f5net.com/v1"
kind: F5BigHttp2Setting
metadata:
name: http2-profile
spec:
activationModes: "alpn"
concurrentStreamsPerConnection: 10
connectionIdleTimeout: 300
frameSize: 2048
insertHeader: false
insertHeaderName: "X-HTTP2"
receiveWindow: 32
writeSize: 16384
headerTableSize: 4096
enforceTlsRequirements: true
---
apiVersion: "k8s.f5net.com/v1"
kind: F5BigHttpSetting
metadata:
name: http-profile
spec:
oneConnect: false
responseChunking: "sustain"
lwsMaxColumn: 80
# kubectl apply -f 14-tls-clientsslsettings.yaml -n cnf-fw-01
# kubectl apply -f 15-http-profiles.yaml -n cnf-fw-01
Step 2: Create DNSApp for DoH service
# cat 16-DNSApp-doh.yaml
apiVersion: "k8s.f5net.com/v1"
kind: F5BigDnsApp
metadata:
name: "cnf-dohapp"
namespace: "cnf-fw-01"
spec:
ipProtocol: "udp"
dohProtocol: "udp"
destination:
address: "10.1.20.100"
port: 443
snat:
type: "automap"
dns:
dnsExpressEnabled: false
dnsCache: "cnf-dnscache"
clientSslSettings: "clientssl-profile"
pool:
members:
- address: "10.1.10.50"
monitors:
dns:
enabled: true
queryName: "www.example.com"
queryType: "a"
recv: "192.168.1.11"
# kubectl apply -f 16-DNSApp-doh.yaml -n cnf-fw-01
Step 3: Testing from our client pod
ubuntu@client:~$ dig @10.1.20.100 -p 443 +https +notls-ca www.google.com
; <<>> DiG 9.18.30-0ubuntu0.20.04.2-Ubuntu <<>> @10.1.20.100 -p 443 +https +notls-ca www.google.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 4935
;; flags: qr rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.google.com. IN A
;; ANSWER SECTION:
www.google.com. 69 IN A 142.251.188.103
www.google.com. 69 IN A 142.251.188.147
www.google.com. 69 IN A 142.251.188.106
www.google.com. 69 IN A 142.251.188.105
www.google.com. 69 IN A 142.251.188.99
www.google.com. 69 IN A 142.251.188.104
;; Query time: 8 msec
;; SERVER: 10.1.20.100#443(10.1.20.100) (HTTPS)
;; WHEN: Thu Jan 22 11:27:05 UTC 2026
;; MSG SIZE rcvd: 139
ubuntu@client:~$ dig @10.1.20.100 -p 443 +https +notls-ca www.example.com
; <<>> DiG 9.18.30-0ubuntu0.20.04.2-Ubuntu <<>> @10.1.20.100 -p 443 +https +notls-ca www.example.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 20401
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.example.com. IN A
;; ANSWER SECTION:
www.example.com. 17723 IN A 192.168.1.11
;; Query time: 4 msec
;; SERVER: 10.1.20.100#443(10.1.20.100) (HTTPS)
;; WHEN: Thu Jan 22 11:27:18 UTC 2026
;; MSG SIZE rcvd: 60
Conclusion
BIG-IP Next DNS CRs transform Kubernetes into a production-grade DNS platform, delivering authoritative resolution, caching efficiency, and encrypted DoH, all while optimizing external traffic costs and hardening security boundaries for cloud-native deployments.
Related Content
Help guide the future of your DevCentral Community!
What tools do you use to collaborate? (1min - anonymous)