
This guide covers deploying BIND9 DNS alongside an already-installed NGINX instance for providing secure DNS services including DoH and DoT. This configuration supports advanced SELinux policies and offloading TLS/HTTPS through NGINX.
1. BIND9 Installation
The default BIND version in RHEL 9 repositories is 9.16 (Extended Support), which does not support DNS-over-TLS (DoT) for forwarders.
To use DoT forwarders (requires BIND 9.19+), consider using the official ISC Fedora COPR repository:
https://copr.fedorainfracloud.org/coprs/isc/bind
For installation instructions, refer to the RHEL documentation:
RHEL 9: Setting up and Configuring BIND
2. SELinux Configuration for BIND9
When installing BIND in a non-standard location (e.g., /opt/isc), SELinux contexts must be explicitly set:
chcon -t named_exec_t /opt/isc/isc-bind/root/usr/sbin/named
Verify Current Allowed Ports:
semanage port -l | grep http_port_t
semanage port -l | grep dns_port_t
Add Custom TCP Ports for Local BIND Listening:
We will use the following ports for local unencrypted access by NGINX:
- TCP 753: DNS-over-TLS local endpoint
- TCP 8458: DoH local endpoint
sudo semanage port -a -t dns_port_t -p tcp 753
sudo semanage port -a -t dns_port_t -p tcp 8458
For NGINX to access TLS-based DNS forwarding:
sudo semanage port -a -t http_port_t -p tcp 853
3. BIND Configuration
http local-http-server { endpoints { "/dns-query"; }; };
options {
listen-on port 753 tls ephemeral { 127.0.0.1; };
http-port 8458;
listen-on port 8458 tls ephemeral http local-http-server { 127.0.0.1; };
allow-query { 127.0.0.1; };
allow-query-cache { localhost; };
allow-recursion { 127.0.0.1; };
recursion yes;
};
Optional: Configure DoT Forwarders (Cloudflare Example)
tls cloudflare-tls { remote-hostname "one.one.one.one"; };
options {
forwarders port 853 {
1.1.1.1 tls cloudflare-tls;
1.0.0.1 tls cloudflare-tls;
};
};
Note: If you are not using DoT, forward on port 53 instead.
4. NGINX Configuration for Reverse Proxying DNS
Install the stream module if not already present:
sudo dnf install nginx-mod-stream
DoH Pass-through (GRPC over HTTP)
http {
upstream doh-servers {
zone doh-servers 64k;
server 127.0.0.1:8458;
keepalive_timeout 60s;
keepalive_requests 100;
keepalive 10;
}
server {
listen 443 ssl http2;
ssl_certificate /etc/ssl/certs/your_cert.pem;
ssl_certificate_key /etc/ssl/private/your_key.key;
location /dns-query {
proxy_set_header Connection "";
grpc_pass grpcs://doh-servers;
}
}
}
DoT and DNS-over-UDP Pass-through (Stream Block)
stream {
upstream dns_servers {
zone dns_servers 64k;
server 127.0.0.1:753;
}
server {
listen 853 ssl;
ssl_certificate /etc/ssl/certs/your_cert.pem;
ssl_certificate_key /etc/ssl/private/your_key.key;
proxy_pass dns_servers;
}
server {
listen 53 udp;
listen 53;
proxy_pass dns_servers;
}
}
5. Firewall Configuration
Allow required ports through firewalld:
sudo firewall-cmd --zone=public --add-port=853/tcp --permanent
sudo firewall-cmd --zone=public --add-port=53/tcp --permanent
sudo firewall-cmd --zone=public --add-port=53/udp --permanent
sudo firewall-cmd --reload
Note: Port 443 is likely already open for HTTPS/DoH traffic.
6. Testing & Debugging
Test DNS responses using dig
and resolvectl
. Verify the NGINX reverse proxy is relaying requests properly. Check logs under:
/var/log/nginx/access.log
/var/log/nginx/error.log
/var/named/data/named.run
(or similar, depending on your BIND config)
Use setenforce 0
only temporarily for debugging SELinux issues. Always return to setenforce 1
and address issues using audit2allow
or direct SELinux rules.
7. Final Notes
This setup results in a high-performance, secure DNS server using modern protocols and strict access controls.
Warning: Do not misuse this infrastructure (e.g., forging DNS responses, inserting root CAs into client devices, etc.). This guide is intended for ethical hosting and self-use scenarios only. Be responsible. Be secure. Be happy.