How To Configure a DNS Proxy on Ubuntu 22.04

Introduction

As you know it all begins with a story. My story is I didn’t want my DNS server directly connected to the internet. I wanted something basic and easy to use.

Say hello to dnsproxy. Its a basic and easy to use DNS proxy. Lets walk through the easy setup. I’ve even provided a script to help with setup. This setup relies on your server having a private IP address and a public IP address.

The Steps

Let’s start by updating Ubuntu. Hopefully you always update before installing new software.
> sudo apt update && sudo apt upgrade -y

Install dnsproxy.
> sudo apt install dnsproxy -y

Create /etc/dnsproxy.conf using the template below. There are place holders in ALL CAPS to highlight what you need to change.

# dnsproxy configuration file for Debian

# Authoritative server
# authoritative 192.168.1.2
authoritative YOUR_DNS_SERVER_IP
authoritative-port 53 # It's port. Defaults to 53.
authoritative-timeout 10 # Seconds to wait for answers.

# Recursive resolver
# recursive 192.168.1.2
recursive YOUR_DNS_SERVER_IP
recursive-port 53 # It's port. Defaults to 53.
recursive-timeout 90 # Seconds to wait for answers.

# Local addresses and ports of dnsproxy
# address and UDP port that dnsproxy use to communicate with users
# listen 8.9.1.2
listen YOUR_PUBLIC_IP
port 53

# address and UDP port that dnsproxy use to communicate with DNS servers.
# if your DNS servers are accessed by different network interfaces, you must
# use 0.0.0.0 as listen_answer. But if they are accessed by the same network
# interface, just set it to the IP address that you have on that interface.
listen_answer 0.0.0.0
port_answer 53003

# Security features
chroot /var/spool/dnsproxy
user dnsproxy

# Internal networks (allowed to do recursive queries)
internal 192.168.0.0/16 # Our internal network
internal 172.16.0.0/12 # Another internal network
internal 10.0.0.0/8 # Friendly neighbours
internal 127.0.0.1
internal YOUR_PRIVATE_IP # Private network
internal YOUR_PUBLIC_IP # Public IP considered as internal

Now start dnsproxy.
> sudo systemctl start dnsproxy

Test that everything is working.

Finally configure dnsproxy to start at boot.
> sudo systemctl enable dnsproxy

A Script To Make Things Easier

I’ve provided a script below that will help you set everything up.

#!/bin/bash
#
# This script sets up dnsproxy on a server.
# The server needs a private and a public network interface
#

#
# Variables
#
config_file="/etc/dnsproxy.conf"

#
# Functions
#

# Function to validate IP address
validate_ip() {
  local ip=$1
  local stat=1

  if [[ $ip =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
    OIFS=$IFS
    IFS='.'
    ip=($ip)
    IFS=$OIFS
    if [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]; then
      stat=0
    fi
  fi
  return $stat
}

#
# Main Script Start
#

# Check if the script is running as root
if [ "$EUID" -ne 0 ]; then
  echo "This script must be run as root. Please run it with sudo or as root."
  exit 1
fi

# Introduction message
echo "This script will create a dnsproxy.conf file for configuring DNS Proxy."
echo "It will also check if dnsproxy is installed, back up any existing config file, and manage the dnsproxy service."
echo ""
read -p "Do you want to continue? (y/n): " continue_script
if [[ $continue_script != "y" && $continue_script != "Y" ]]; then
  echo "Exiting script."
  exit 0
fi

# Check if dnsproxy is installed
if ! command -v dnsproxy &> /dev/null; then
  echo "dnsproxy is not installed."
  read -p "Do you want to install dnsproxy? (y/n): " install_dnsproxy
  if [[ $install_dnsproxy == "y" || $install_dnsproxy == "Y" ]]; then
    echo "Installing dnsproxy..."
    if ! sudo apt-get update && sudo apt-get install -y dnsproxy; then
      echo "Failed to install dnsproxy. Continuing without it..."
    fi
  else
    echo "Continuing without installing dnsproxy..."
  fi
fi

# Prompt for public interface IP
default_public_ip="8.9.1.2"
while true; do
  read -p "Enter the public interface IP address [default: $default_public_ip]: " public_ip
  public_ip=${public_ip:-$default_public_ip}
  validate_ip $public_ip
  if [[ $? -eq 0 ]]; then
    break
  else
    echo "Invalid IP address. Please enter a valid IP address."
  fi
done

# Prompt for private interface IP
default_private_ip="192.168.1.2"
while true; do
  read -p "Enter the private interface IP address [default: $default_private_ip]: " private_ip
  private_ip=${private_ip:-$default_private_ip}
  validate_ip $private_ip
  if [[ $? -eq 0 ]]; then
    break
  else
    echo "Invalid IP address. Please enter a valid IP address."
  fi
done

# Prompt for authoritative DNS server IP
while true; do
  read -p "Enter the IP address of the authoritative DNS server: " auth_dns_ip
  validate_ip $auth_dns_ip
  if [[ $? -eq 0 ]]; then
    break
  else
    echo "Invalid IP address. Please enter a valid IP address."
  fi
done

# Prompt for recursive DNS server IP
while true; do
  read -p "Enter the IP address of the recursive DNS server: " rec_dns_ip
  validate_ip $rec_dns_ip
  if [[ $? -eq 0 ]]; then
    break
  else
    echo "Invalid IP address. Please enter a valid IP address."
  fi
done

# Backup existing config file if it exists
if [ -f "$config_file" ]; then
  timestamp=$(date +%Y%m%d%H%M%S)
  backup_file="${config_file}.${timestamp}.bak"
  cp "$config_file" "$backup_file"
  echo "Existing config file backed up as $backup_file"
fi

# Create dnsproxy.conf file
cat < $config_file
# dnsproxy configuration file example for Debian

# Authoritative server
authoritative        $auth_dns_ip
authoritative-port   53              # It's port. Defaults to 53.
authoritative-timeout 10             # Seconds to wait for answers.

# Recursive resolver
recursive            $rec_dns_ip
recursive-port       53              # It's port. Defaults to 53.
recursive-timeout    90              # Seconds to wait for answers.

# Local addresses and ports of dnsproxy
# address and UDP port that dnsproxy use to communicate with users
listen $public_ip
port 53

# address and UDP port that dnsproxy use to communicate with DNS servers.
# if your DNS servers are accessed by different network interfaces, you must
# use 0.0.0.0 as listen_answer. But if they are accessed by the same network
# interface, just set it to the IP address that you have on that interface.
listen_answer 0.0.0.0
port_answer 53003

# Security features
chroot /var/spool/dnsproxy
user dnsproxy

# Internal networks (allowed to do recursive queries)
internal 192.168.0.0/16          # Our internal network
internal 172.16.0.0/12           # Another internal network
internal 10.0.0.0/8              # Friendly neighbours
internal 127.0.0.1
internal $private_ip             # Private network
internal $public_ip              # Public IP considered as internal
EOF

echo "dnsproxy.conf file created successfully."

# Check if dnsproxy is running
if pgrep dnsproxy > /dev/null; then
  echo "dnsproxy is currently running. Restarting it..."
  sudo systemctl restart dnsproxy
  echo "dnsproxy has been restarted."
else
  read -p "dnsproxy is not running. Do you want to start it? (y/n): " start_dnsproxy
  if [[ $start_dnsproxy == "y" || $start_dnsproxy == "Y" ]]; then
    echo "Starting dnsproxy..."
    sudo systemctl start dnsproxy
    sudo systemctl enable dnsproxy
    echo "dnsproxy has been started and set to start on boot."
  else
    echo "dnsproxy was not started."
  fi
fi

# Script end
echo "dnsproxy setup complete."
exit 0

You can create an empty file for pasting the script into.
> nano -w setup_dnsproxy.sh

Copy the script from the website and paste it into nano. When done exit namo and save changes.

Use the following command to run the script.
> sudo bash setup_dnsproxy.sh

Conclusion

That’s it! A quick and easy DNS proxy setup.