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.

HOWTO CentOS 7.x Base Server Setup

Introduction

All of our servers will start with this install. This base server is based on CentOS 7.

Downloading the ISO

Visit the CentOS website and download the Minimal install ISO.

Initial Install

Boot the install DVD.

The graphical install loads and we’re ready to go.
*Choose your language and click next.
The next screen has a menu with groups of settings that need to be configured.
*Click on ‘DATE & TIME’ and set your timezone.
*Click on ‘INSTALLATION DESTINATION’ and click done to let it auto partition the drive.
*Click on ‘NETWORK & HOSTNAME’ and set the hostname. Click the ‘Configure’ button and set the adaptor to auto connect. Then click done.
*Click on ‘SOFTWARE SELECTION’. Select ‘Minimal Install’. Check the option for ‘Compatibility Libraries’. Then click done.
*When done click ‘Begin Installation’.
The installation progress screen has a couple of menu items for setting the root password and creating users.
*set the root password.
*You should create a user to admin the system.
*Click ‘Finish Configuration’ when it appears.
*Click ‘Reboot’ when it appears.

First boot

Reboot the machine when the install finishes.
The OS will boot. Log in.

Get everything updated and install a couple of items.
> yum -y install nano net-tools deltarpm
> yum -y group install ‘Infrastructure Server’
> yum -y upgrade

Disable selinux

Now we need to disable selinux. There is some software that doesn’t play well with selinux.
Edit /etc/selinux/config and change SELINUX=enforcing to SELINUX=disabled

Disable the firewall

> systemctl stop firewalld
> systemctl disable firewalld

WARNING: My server isn’t directly connected to the internet. The firewall is disabled to help with installation, configuration and testing easier. Once everything is working, turn on the firewall and configure it. I wil remind you to secure your server at the end of this howto.

now reboot the server.

The Second Boot – Installing Additional Packages

We need quite a few other packages. A change in this howto is that I’m installing RPMs reguardless if they were already installed by another dependency. This guards against RPM changes that could cause a package to not be installed.

We need to add and enable a few repositories.
Type nano -w /etc/yum.repos.d/CentOS-Base.repo
For the centosplus section change ‘enable=0’. Change the 0 to a 1.

We need the webmin repo. Create webmin.repo with the text below.
> nano -w /etc/yum.repos.d/webmin.repo

[Webmin]
 name=Webmin Distribution Neutral
 #baseurl=http://download.webmin.com/download/yum
 mirrorlist=http://download.webmin.com/download/yum/mirrorlist
 enabled=1

And the EPEL repo.
> yum install epel-release

Finish up by installing hte remi repo.
> wget http://rpms.remirepo.net/enterprise/remi-release-7.rpm
> rpm -Uvh remi-release-7.rpm

Edit /etc/yum.repos.d/remi.repo. Change enable=0 to enable=1 in the sections “remi” and “remi-php56”.

Now bring everything up to date.
> yum -y update

Install the following RPMs. Multiple lines to make cut and paste easier.
> yum -y install gcc gcc-c++ wget bison nano make createrepo screen
> yum -y install libmcrypt caching-nameserver

Now lets install webmin. We need SSL support in perl. Setup is easier if you get this installed before webmin.
> yum -y install perl-Net-SSLeay
> rpm –import http://www.webmin.com/jcameron-key.asc
> yum -y install webmin
> systemctl enable webmin
> service webmin start

Install MariaDB.
> yum -y install mariadb-server mariadb
> systemctl enable mariadb
> systemctl start mariadb

Run the following script to setup mariadb.
> mysql_secure_installation

Install PHP.
> yum -y install php php-cli php-fpm
> yum -y install php-gd php-ncurses php-snmp php-mbstring php-mysql php-devel
> yum -y install php-odbc php-imap php-pecl-apc
> yum -y install php-xmlrpc php-dba php-pear-DB php-mcrypt
> systemctl enable php-fpm
> systemctl start php-fpm

Install Apache web server
> yum -y install @web-server
> systemctl enable httpd
> systemctl start httpd

Installing and Configuring phpMyAdmin

I prefer to phpMyAdmin to manage my MySQL databases.

Now install phpMyAdmin.
> yum -y install phpMyAdmin

You will need to add access to phpMyAdmin. By default only the local server can access it. Edit /etc/httpd/conf.d/phpMyAdmin.conf to look like the following.

# phpMyAdmin - Web based MySQL browser written in php
# 
# Allows only localhost by default
#
# But allowing phpMyAdmin to anyone other than localhost should be considered
# dangerous unless properly secured by SSL

Alias /phpMyAdmin /usr/share/phpMyAdmin
Alias /phpmyadmin /usr/share/phpMyAdmin

<Directory /usr/share/phpMyAdmin/>
 AddDefaultCharset UTF-8

<IfModule mod_authz_core.c>
 # Apache 2.4
 Require local
 Require ip 192.168.0.0/16
 Require ip 10.0.0.0/8
 </IfModule>
 <IfModule !mod_authz_core.c>
 # Apache 2.2
 Order Deny,Allow
 Deny from All
 Allow from 127.0.0.1
 Allow from ::1
 </IfModule>
</Directory>

<Directory /usr/share/phpMyAdmin/setup/>
 <IfModule mod_authz_core.c>
 # Apache 2.4
 Require local
 Require ip 192.168.0.0/16
 Require ip 10.0.0.0/8
 </IfModule>
 <IfModule !mod_authz_core.c>
 # Apache 2.2
 Order Deny,Allow
 Deny from All
 Allow from 127.0.0.1
 Allow from 192.168. 
 Allow from 10.
 Allow from ::1
 </IfModule>
</Directory>

# These directories do not require access over HTTP - taken from the original
# phpMyAdmin upstream tarball
#
<Directory /usr/share/phpMyAdmin/libraries/>
 Order Deny,Allow
 Deny from All
 Allow from None
</Directory>

<Directory /usr/share/phpMyAdmin/setup/lib/>
 Order Deny,Allow
 Deny from All
 Allow from None
</Directory>

<Directory /usr/share/phpMyAdmin/setup/frames/>
 Order Deny,Allow
 Deny from All
 Allow from None
</Directory>

# This configuration prevents mod_security at phpMyAdmin directories from
# filtering SQL etc. This may break your mod_security implementation.
#
#<IfModule mod_security.c>
# <Directory /usr/share/phpMyAdmin/>
# SecRuleInheritance Off
# </Directory>
#</IfModule>

Restart Apache.
> systemctl restart httpd

Now test it out.

Installing cockpit

I’m trying cockpit as my server admin tool.  Do the following to set it up.
> yum -y install cockpit cockpit-dashboard cockpit-networkmanager cockpit-packagekit
> yum -y install cockpit-selinux cockpit-sosreport cockpit-storaged
> systemctl start cockpit
> systemctl enable cockpit.socket

You can now login to https://yourserver.tld:9090 to administer your server.

Getting root’s and other’s mail

You need to get some local system user’s mail. We’ll use postfix’s virtual file to get the emails to the right place.

Add the following to /etc/postfix/virtual

root admin@yourdomain.tld
 postmaster admin@yourdomain.tld
 abuse admin@yourdomain.tld

Now add the configuration option to main.cf
> postconf -e “virtual_alias_maps = hash:/etc/postfix/virtual”
Just a couple commands to wrap everything up.
> postmap /etc/postfix/virtual
> systemctl restart postfix

Final Settings

You may want to enable the linux firewall.
Set your timezone in /etc/php.ini

Conclusion

That’s it for the basic server setup. This is an example of a standard linux server setup. Be sure to use setup or webmin to set which services you want to start at boot time. See the other pages for info on configuring servers for virtual webhosting or virtual email hosting. Remember to configure the firewall on the server.