DNS Container and docker compose

This commit is contained in:
Eduardo Silva 2024-04-29 15:26:30 -03:00
parent da1513e560
commit aefd3f698b
18 changed files with 175 additions and 20 deletions

View File

@ -4,6 +4,7 @@ IMAGES=(
"eduardosilva/wireguard_webadmin:latest"
"eduardosilva/wireguard_webadmin_cron:latest"
"eduardosilva/wireguard_webadmin_nginx:latest"
"eduardosilva/wireguard_webadmin_dns:latest"
)
build_images() {

View File

@ -0,0 +1,13 @@
FROM ubuntu:latest
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \
apt-get install -y dnsmasq nano inotify-tools psmisc && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

View File

@ -0,0 +1,40 @@
#!/bin/bash
CONFIG_FILE="/etc/dnsmasq/wireguard_webadmin_dns.conf"
DEFAULT_CONFIG_CONTENT="
no-dhcp-interface=
server=1.1.1.1
server=1.0.0.1
listen-address=0.0.0.0
bind-interfaces
"
create_default_config() {
if [ ! -f "$CONFIG_FILE" ]; then
echo "Config file not found, creating a new one..."
echo "$DEFAULT_CONFIG_CONTENT" > "$CONFIG_FILE"
fi
}
start_dnsmasq() {
dnsmasq -C "$CONFIG_FILE" &
while inotifywait -e modify "$CONFIG_FILE"; do
echo "Configuration changed, reloading dnsmasq..."
pkill dnsmasq
sleep 5
dnsmasq -C "$CONFIG_FILE" &
done
}
handle_sigint() {
echo "SIGINT received. Stopping inotifywait and dnsmasq..."
pkill inotifywait
pkill dnsmasq
exit 0
}
trap handle_sigint SIGINT
create_default_config
start_dnsmasq

View File

@ -21,6 +21,7 @@ class DNSSettingsForm(forms.ModelForm):
self.helper = FormHelper()
self.fields['dns_primary'].label = 'Primary Resolver'
self.fields['dns_secondary'].label = 'Secondary Resolver'
self.fields['dns_primary'].required = True
self.helper.form_method = 'post'
self.helper.layout = Layout(
Fieldset(

View File

@ -25,9 +25,51 @@ server:
local-zone: "local." static
do-not-query-localhost: {do_not_query_localhost}
verbosity: 1
recursion: yes
'''
unbound_config += forward_zone
if static_hosts:
unbound_config += '\nlocal-zone: "." transparent\n'
for static_host in static_hosts:
unbound_config += f'local-data: "{static_host.hostname}. IN A {static_host.ip_address}"\n'
unbound_config += f' local-data: "{static_host.hostname}. IN A {static_host.ip_address}"\n'
return unbound_config
def generate_dnsdist_config():
dns_settings = DNSSettings.objects.get(name='dns_settings')
static_hosts = StaticHost.objects.all()
dnsdist_config = "setLocal('0.0.0.0:53')\n"
dnsdist_config += "setACL('0.0.0.0/0')\n"
if dns_settings.dns_primary:
dnsdist_config += f"newServer({{address='{dns_settings.dns_primary}', pool='upstreams'}})\n"
if dns_settings.dns_secondary:
dnsdist_config += f"newServer({{address='{dns_settings.dns_secondary}', pool='upstreams'}})\n"
if static_hosts:
dnsdist_config += "addAction(makeRule(''), PoolAction('staticHosts'))\n"
for static_host in static_hosts:
dnsdist_config += f"addLocal('{static_host.hostname}', '{static_host.ip_address}')\n"
return dnsdist_config
def generate_dnsmasq_config():
dns_settings = DNSSettings.objects.get(name='dns_settings')
static_hosts = StaticHost.objects.all()
dnsmasq_config = f'''
no-dhcp-interface=
listen-address=0.0.0.0
bind-interfaces
'''
if dns_settings.dns_primary:
dnsmasq_config += f'server={dns_settings.dns_primary}\n'
if dns_settings.dns_secondary:
dnsmasq_config += f'server={dns_settings.dns_secondary}\n'
if static_hosts:
dnsmasq_config += '\n'
for static_host in static_hosts:
dnsmasq_config += f'address=/{static_host.hostname}/{static_host.ip_address}\n'
return dnsmasq_config

View File

@ -4,7 +4,7 @@ from django.contrib import messages
from user_manager.models import UserAcl
from .models import DNSSettings, StaticHost
from .forms import StaticHostForm, DNSSettingsForm
from .functions import generate_unbound_config
from .functions import generate_dnsmasq_config
from django.conf import settings
@ -13,9 +13,9 @@ def view_apply_dns_config(request):
dns_settings, _ = DNSSettings.objects.get_or_create(name='dns_settings')
dns_settings.pending_changes = False
dns_settings.save()
unbound_config = generate_unbound_config()
with open(settings.UNBOUND_CONFIG, 'w') as f:
f.write(unbound_config)
dnsmasq_config = generate_dnsmasq_config()
with open(settings.DNS_CONFIG_FILE, 'w') as f:
f.write(dnsmasq_config)
messages.success(request, 'DNS settings applied successfully')
return redirect('/dns/')
@ -48,12 +48,7 @@ def view_manage_dns_settings(request):
<p>
All DNS queries will be forwarded to the primary resolver. If the primary resolver is not available, the secondary resolver will be used.
</p>
<strong>
Local DNS Resolution
</strong>
<p>
If no forwarders are specified, the system will locally resolve DNS queries. This can lead to slower DNS resolution times, but can be useful in certain scenarios.
</p>
'''
context = {

View File

@ -11,6 +11,12 @@ services:
context: ./cron
dockerfile: Dockerfile-cron
wireguard-webadmin-dns:
image: eduardosilva/wireguard_webadmin_dns:latest
build:
context: ./containers/dnsmasq
dockerfile: Dockerfile-dnsmasq
wireguard-webadmin-nginx:
image: eduardosilva/wireguard_webadmin_nginx:latest
build:

View File

@ -8,10 +8,12 @@ services:
environment:
- SERVER_ADDRESS=127.0.0.1
- DEBUG_MODE=True
- COMPOSE_VERSION=02b
volumes:
- wireguard:/etc/wireguard
- static_volume:/app_static_files/
- .:/app
- dnsmasq_conf:/etc/dnsmasq
ports:
# Do not directly expose the Django port to the internet, use some kind of reverse proxy with SSL.
- "127.0.0.1:8000:8000"
@ -38,6 +40,16 @@ services:
depends_on:
- wireguard-webadmin
wireguard-webadmin-dns:
container_name: wireguard-webadmin-dns
restart: unless-stopped
build:
context: ./containers/dnsmasq
dockerfile: Dockerfile-dnsmasq
volumes:
- dnsmasq_conf:/etc/dnsmasq/
volumes:
static_volume:
wireguard:
dnsmasq_conf:

View File

@ -7,9 +7,11 @@ services:
environment:
- SERVER_ADDRESS=127.0.0.1
- DEBUG_MODE=True
- COMPOSE_VERSION=02b
volumes:
- wireguard:/etc/wireguard
- static_volume:/app_static_files/
- dnsmasq_conf:/etc/dnsmasq
ports:
# Do not directly expose the Django port to the internet, use some kind of reverse proxy with SSL.
- "127.0.0.1:8000:8000"
@ -33,6 +35,14 @@ services:
depends_on:
- wireguard-webadmin
wireguard-webadmin-dns:
container_name: wireguard-webadmin-dns
restart: unless-stopped
image: eduardosilva/wireguard_webadmin_dns:latest
volumes:
- dnsmasq_conf:/etc/dnsmasq/
volumes:
static_volume:
wireguard:
dnsmasq_conf:

View File

@ -7,9 +7,11 @@ services:
environment:
- SERVER_ADDRESS=${SERVER_ADDRESS}
- DEBUG_MODE=${DEBUG_MODE}
- COMPOSE_VERSION=02b
volumes:
- wireguard:/etc/wireguard
- static_volume:/app_static_files/
- dnsmasq_conf:/etc/dnsmasq
ports:
# Do not directly expose the Django port to the internet, use the reverse proxy below instead
# - "127.0.0.1:8000:8000"
@ -33,6 +35,13 @@ services:
depends_on:
- wireguard-webadmin
wireguard-webadmin-dns:
container_name: wireguard-webadmin-dns
restart: unless-stopped
image: eduardosilva/wireguard_webadmin_dns:latest
volumes:
- dnsmasq_conf:/etc/dnsmasq/
wireguard-webadmin-nginx:
container_name: wireguard-webadmin-nginx
restart: unless-stopped
@ -48,3 +57,4 @@ volumes:
static_volume:
https_cert:
wireguard:
dnsmasq_conf:

View File

@ -2,6 +2,11 @@
set -e
if [[ "$COMPOSE_VERSION" != "02b" ]]; then
echo "ERROR: Please upgrade your docker compose file. Exiting."
exit 1
fi
if [ -z "$SERVER_ADDRESS" ]; then
echo "SERVER_ADDRESS environment variable is not set. Exiting."
exit 1

View File

@ -143,6 +143,19 @@ def export_user_firewall():
return "".join(iptables_rules)
def generate_redirect_dns_rules():
wireguard_instance_list = WireGuardInstance.objects.all()
firewall_settings, firewall_settings_created = FirewallSettings.objects.get_or_create(name='global')
dns_redirect_rules = ''
for wireguard_instance in wireguard_instance_list:
dns_redirect_rules += f"# DNS Redirect for instance wg{wireguard_instance.instance_id}\n"
dns_redirect_rules += f"iptables -t nat -A WGWADM_PREROUTING -i wg{wireguard_instance.instance_id} -d {wireguard_instance.address} -p udp --dport 53 -j DNAT --to $DNS_IP:53\n"
dns_redirect_rules += f"iptables -t nat -A WGWADM_PREROUTING -i wg{wireguard_instance.instance_id} -d {wireguard_instance.address} -p tcp --dport 53 -j DNAT --to $DNS_IP:53\n"
dns_redirect_rules += f"iptables -t nat -A WGWADM_POSTROUTING -i wg{wireguard_instance.instance_id} -o {firewall_settings.wan_interface} -d $DNS_IP -j MASQUERADE\n"
dns_redirect_rules += f"iptables -t filter -A WGWADM_FORWARD -i wg{wireguard_instance.instance_id} -o {firewall_settings.wan_interface} -d $DNS_IP -j ACCEPT\n"
return dns_redirect_rules
def generate_firewall_header():
firewall_settings, firewall_settings_created = FirewallSettings.objects.get_or_create(name='global')
header = f'''#!/bin/bash
@ -151,6 +164,10 @@ def generate_firewall_header():
#
# This script was generated by WireGuard_WebAdmin on {timezone.now().strftime('%Y-%m-%d %H:%M:%S %Z')}
#
DNS_IP=$(host wireguard-webadmin-dns | grep -oP 'has address \K[\d\.]+')
if [ -z "$DNS_IP" ]; then
DNS_IP="127.0.0.250"
fi
iptables -t nat -N WGWADM_POSTROUTING >> /dev/null 2>&1
iptables -t nat -N WGWADM_PREROUTING >> /dev/null 2>&1

View File

@ -98,11 +98,11 @@
<div class="form-row">
<div class="form-group col-md-6">
<label for="{{ form.dns_primary.id_for_label }}">{{ form.dns_primary.label }}</label>
<input type="text" class="form-control" id="{{ form.dns_primary.id_for_label }}" name="{{ form.dns_primary.html_name }}" placeholder="1.1.1.1" value="{{ form.dns_primary.value|default_if_none:'' }}">
<input type="text" class="form-control" id="{{ form.dns_primary.id_for_label }}" name="{{ form.dns_primary.html_name }}" value="{{ form.dns_primary.value|default_if_none:'' }}">
</div>
<div class="form-group col-md-6">
<label for="{{ form.dns_secondary.id_for_label }}">{{ form.dns_secondary.label }}</label>
<input type="text" class="form-control" id="{{ form.dns_secondary.id_for_label }}" name="{{ form.dns_secondary.html_name }}" placeholder="1.0.0.1" value="{{ form.dns_secondary.value|default_if_none:'' }}">
<input type="text" class="form-control" id="{{ form.dns_secondary.id_for_label }}" name="{{ form.dns_secondary.html_name }}" value="{{ form.dns_secondary.value|default_if_none:'' }}">
</div>
</div>

View File

@ -16,7 +16,7 @@ class WireGuardInstanceForm(forms.ModelForm):
post_down = forms.CharField(label='Post Down', required=False)
peer_list_refresh_interval = forms.IntegerField(label='Web Refresh Interval', initial=20)
dns_primary = forms.GenericIPAddressField(label='Primary DNS', initial='1.1.1.1', required=False)
dns_secondary = forms.GenericIPAddressField(label='Secondary DNS', initial='1.0.0.1', required=False)
dns_secondary = forms.GenericIPAddressField(label='Secondary DNS', initial='', required=False)
class Meta:
model = WireGuardInstance

View File

@ -60,6 +60,7 @@ def generate_instance_defaults():
'private_key': new_private_key,
'public_key': new_public_key,
'address': new_address,
'dns_primary': new_address,
'netmask': 24,
'persistent_keepalive': 25,
'hostname': 'myserver.example.com',

View File

@ -4,7 +4,8 @@ import qrcode
import subprocess
from django.http import HttpResponse
from django.shortcuts import redirect, get_object_or_404, render
from firewall.tools import generate_firewall_header, generate_firewall_footer, generate_port_forward_firewall, export_user_firewall
from firewall.tools import generate_firewall_header, generate_firewall_footer, generate_port_forward_firewall, \
export_user_firewall, generate_redirect_dns_rules
from user_manager.models import UserAcl
from wireguard.models import WireGuardInstance, Peer, PeerAllowedIP
from firewall.models import RedirectRule
@ -55,6 +56,7 @@ def generate_peer_config(peer_uuid):
def export_firewall_configuration():
firewall_content = generate_firewall_header()
firewall_content += generate_redirect_dns_rules()
firewall_content += generate_port_forward_firewall()
firewall_content += export_user_firewall()
firewall_content += generate_firewall_footer()

View File

@ -131,9 +131,9 @@ STATICFILES_DIRS = [
# Default primary key field type
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
UNBOUND_CONFIG = '/config/unbound.conf'
DNS_CONFIG_FILE = '/etc/dnsmasq/wireguard_webadmin_dns.conf'
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
WIREGUARD_WEBADMIN_VERSION = 9607
WIREGUARD_WEBADMIN_VERSION = 9609
from wireguard_webadmin.production_settings import *

View File

@ -28,7 +28,7 @@ from dns.views import view_static_host_list, view_manage_static_host, view_manag
urlpatterns = [
path('admin/', admin.site.urls),
# path('admin/', admin.site.urls),
path('', view_welcome, name='welcome'),
path('status/', view_wireguard_status, name='wireguard_status'),
path('dns/', view_static_host_list, name='static_host_list'),