diff --git a/dns/admin.py b/dns/admin.py index 8c38f3f..48e1202 100644 --- a/dns/admin.py +++ b/dns/admin.py @@ -1,3 +1,26 @@ from django.contrib import admin -# Register your models here. +from .models import DNSFilterList, DNSSettings, StaticHost + + +class DNSFilterListAdmin(admin.ModelAdmin): + list_display = ('name', 'description', 'enabled', 'list_url', 'host_count', 'created', 'updated') + list_filter = ('enabled', 'created', 'updated') + search_fields = ('name', 'description', 'list_url') + ordering = ('name', 'created') +admin.site.register(DNSFilterList, DNSFilterListAdmin) + + +class DNSSettingsAdmin(admin.ModelAdmin): + list_display = ('dns_primary', 'dns_secondary', 'pending_changes', 'created', 'updated') + list_filter = ('pending_changes', 'created', 'updated') + search_fields = ('dns_primary', 'dns_secondary') + ordering = ('created', 'updated') +admin.site.register(DNSSettings, DNSSettingsAdmin) + + +class StaticHostAdmin(admin.ModelAdmin): + list_display = ('hostname', 'ip_address', 'created', 'updated') + search_fields = ('hostname', 'ip_address') + ordering = ('hostname', 'created') +admin.site.register(StaticHost, StaticHostAdmin) \ No newline at end of file diff --git a/dns/forms.py b/dns/forms.py index bf4acb4..16495b3 100644 --- a/dns/forms.py +++ b/dns/forms.py @@ -1,15 +1,14 @@ -import requests -from .models import DNSSettings, StaticHost - -from django import forms -from crispy_forms.helper import FormHelper -from crispy_forms.layout import Layout, Fieldset, ButtonHolder, Submit, Div, Field, HTML -from crispy_forms.bootstrap import FormActions, StrictButton -from django.core.exceptions import ValidationError -from datetime import datetime -from django.core.exceptions import ValidationError import re +from crispy_forms.bootstrap import FormActions +from crispy_forms.helper import FormHelper +from crispy_forms.layout import Layout, Fieldset, Div, Field, Submit, HTML +from django import forms +from django.core.exceptions import ValidationError + +from .models import DNSFilterList +from .models import DNSSettings, StaticHost + class DNSSettingsForm(forms.ModelForm): class Meta: @@ -89,3 +88,39 @@ class StaticHostForm(forms.ModelForm): if not re.match(regex, hostname): raise ValidationError('Invalid hostname') return + + +class DNSFilterListForm(forms.ModelForm): + class Meta: + model = DNSFilterList + # Only allow editable fields + fields = ['name', 'description', 'list_url'] + + def __init__(self, *args, **kwargs): + super(DNSFilterListForm, self).__init__(*args, **kwargs) + self.helper = FormHelper() + self.helper.form_method = 'post' + # Add a delete button if editing an existing instance + if self.instance.pk: + delete_html = ( + "Delete" + ) + else: + delete_html = '' + self.helper.layout = Layout( + Fieldset( + 'DNS Filter List Details', + Div( + Div(Field('name', css_class='form-control'), css_class='col-md-12'), + Div(Field('description', css_class='form-control'), css_class='col-md-12'), + Div(Field('list_url', css_class='form-control'), css_class='col-md-12'), + css_class='row' + ), + ), + FormActions( + Submit('save', 'Save', css_class='btn btn-primary'), + HTML('Back'), + HTML(delete_html), + ) + ) diff --git a/dns/migrations/0002_dnsfilterlist.py b/dns/migrations/0002_dnsfilterlist.py new file mode 100644 index 0000000..c23dc12 --- /dev/null +++ b/dns/migrations/0002_dnsfilterlist.py @@ -0,0 +1,29 @@ +# Generated by Django 5.1.5 on 2025-03-01 21:05 + +import uuid + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dns', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='DNSFilterList', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.SlugField(max_length=100, unique=True)), + ('description', models.CharField(max_length=100)), + ('enabled', models.BooleanField(default=False)), + ('list_url', models.URLField()), + ('last_updated', models.DateTimeField(blank=True, null=True)), + ('created', models.DateTimeField(auto_now_add=True)), + ('updated', models.DateTimeField(auto_now=True)), + ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)), + ], + ), + ] diff --git a/dns/migrations/0003_dnsfilterlist_host_count.py b/dns/migrations/0003_dnsfilterlist_host_count.py new file mode 100644 index 0000000..8bf5fd1 --- /dev/null +++ b/dns/migrations/0003_dnsfilterlist_host_count.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.5 on 2025-03-01 21:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dns', '0002_dnsfilterlist'), + ] + + operations = [ + migrations.AddField( + model_name='dnsfilterlist', + name='host_count', + field=models.IntegerField(default=0), + ), + ] diff --git a/dns/models.py b/dns/models.py index af74a49..02cd831 100644 --- a/dns/models.py +++ b/dns/models.py @@ -1,6 +1,7 @@ -from django.db import models import uuid +from django.db import models + class DNSSettings(models.Model): name = models.CharField(default='dns_settings', max_length=100) @@ -23,3 +24,19 @@ class StaticHost(models.Model): def __str__(self): return self.hostname + + +class DNSFilterList(models.Model): + name = models.SlugField(max_length=100, unique=True) + description = models.CharField(max_length=100) + enabled = models.BooleanField(default=False) + list_url = models.URLField() + last_updated = models.DateTimeField(blank=True, null=True) + host_count = models.IntegerField(default=0) + + created = models.DateTimeField(auto_now_add=True) + updated = models.DateTimeField(auto_now=True) + uuid = models.UUIDField(unique=True, default=uuid.uuid4, editable=False) + + def __str__(self): + return self.name \ No newline at end of file diff --git a/dns/views.py b/dns/views.py index 349e31d..cbd681b 100644 --- a/dns/views.py +++ b/dns/views.py @@ -1,11 +1,14 @@ +from django.conf import settings +from django.contrib import messages from django.contrib.auth.decorators import login_required from django.shortcuts import render, get_object_or_404, redirect -from django.contrib import messages + from user_manager.models import UserAcl -from .models import DNSSettings, StaticHost +from .forms import DNSFilterListForm from .forms import StaticHostForm, DNSSettingsForm from .functions import generate_dnsmasq_config -from django.conf import settings +from .models import DNSSettings, DNSFilterList +from .models import StaticHost def export_dns_configuration(): @@ -29,11 +32,21 @@ def view_apply_dns_config(request): def view_static_host_list(request): dns_settings, _ = DNSSettings.objects.get_or_create(name='dns_settings') static_host_list = StaticHost.objects.all().order_by('hostname') + filter_lists = DNSFilterList.objects.all().order_by('name') + if not filter_lists: + DNSFilterList.objects.create( + name='stevenblack-hosts', list_url='https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts', + description='adware and malware domains', enabled=False + ) + filter_lists = DNSFilterList.objects.all().order_by('name') + messages.success(request, 'Default DNS Filter List created successfully') + if dns_settings.pending_changes: messages.warning(request, 'Pending Changes|There are pending DNS changes that have not been applied') context = { 'dns_settings': dns_settings, 'static_host_list': static_host_list, + 'filter_lists': filter_lists } return render(request, 'dns/static_host_list.html', context=context) @@ -101,3 +114,42 @@ def view_manage_static_host(request): 'instance': static_dns, } return render(request, 'generic_form.html', context=context) + + +@login_required +def view_manage_filter_list(request): + if not UserAcl.objects.filter(user=request.user, user_level__gte=50).exists(): + return render(request, 'access_denied.html', {'page_title': 'Access Denied'}) + + dns_settings, _ = DNSSettings.objects.get_or_create(name='dns_settings') + + if request.GET.get('uuid'): + filter_list = get_object_or_404(DNSFilterList, uuid=request.GET.get('uuid')) + if request.GET.get('action') == 'delete': + if request.GET.get('confirmation') == 'delete': + if filter_list.enabled: + messages.warning(request, 'DNS Filter List not deleted | Filter List is enabled') + return redirect('/dns/') + filter_list.delete() + messages.success(request, 'DNS Filter List deleted successfully') + return redirect('/dns/') + else: + messages.warning(request, 'DNS Filter List not deleted | Invalid confirmation') + return redirect('/dns/') + else: + filter_list = None + + form = DNSFilterListForm(request.POST or None, instance=filter_list) + if form.is_valid(): + form.save() + dns_settings.pending_changes = True + dns_settings.save() + messages.success(request, 'DNS Filter List saved successfully') + return redirect('/dns/') + + context = { + 'dns_settings': dns_settings, + 'form': form, + 'instance': filter_list, + } + return render(request, 'generic_form.html', context=context) \ No newline at end of file diff --git a/templates/dns/static_host_list.html b/templates/dns/static_host_list.html index 2a7a04b..9479804 100644 --- a/templates/dns/static_host_list.html +++ b/templates/dns/static_host_list.html @@ -7,46 +7,105 @@

Static Host List

-
-
-
- - - - - - - - - - {% if static_host_list %} - {% for static_host in static_host_list %} +
+
+
HostnameIP Address
+ - - - + + + - {% endfor %} - {% else %} - - - - {% endif %} - -
{{ static_host.hostname }}{{ static_host.ip_address }} - - HostnameIP Address
+ + + {% if static_host_list %} + {% for static_host in static_host_list %} + + {{ static_host.hostname }} + {{ static_host.ip_address }} + + + + + {% endfor %} + {% else %} + + + + {% endif %} + + +
+
+ +
+
- -
-
- Add Static Host - DNS Settings - Apply Config -
+
+ + + +
+
+
+
+

DNS Filter Lists

+
+
+
+ + + + + + + + + + + + + + {% if filter_lists %} + {% for filter_list in filter_lists %} + + + + + + + + + + {% endfor %} + {% else %} + + + + {% endif %} + +
NameDescriptionHostsLast Update
{{ filter_list.name }}{{ filter_list.description }}{{ filter_list.host_count }}{{ filter_list.last_updated|default_if_none:"" }} + + + + + + {% comment %}{% endcomment %} +
+
+
+
+ +
diff --git a/wireguard_webadmin/urls.py b/wireguard_webadmin/urls.py index 36f8c4e..cc60066 100644 --- a/wireguard_webadmin/urls.py +++ b/wireguard_webadmin/urls.py @@ -21,7 +21,8 @@ from accounts.views import view_create_first_user, view_login, view_logout from api.views import wireguard_status, cron_check_updates, cron_update_peer_latest_handshake, \ routerfleet_get_user_token, routerfleet_authenticate_session, peer_info, api_peer_invite from console.views import view_console -from dns.views import view_static_host_list, view_manage_static_host, view_manage_dns_settings, view_apply_dns_config +from dns.views import view_static_host_list, view_manage_static_host, view_manage_dns_settings, view_apply_dns_config, \ + view_manage_filter_list from firewall.views import view_redirect_rule_list, manage_redirect_rule, view_firewall_rule_list, manage_firewall_rule, \ view_manage_firewall_settings, view_generate_iptables_script, view_reset_firewall, view_firewall_migration_required from user_manager.views import view_user_list, view_manage_user, view_peer_group_list, view_peer_group_manage @@ -41,6 +42,7 @@ urlpatterns = [ path('dns/apply_config/', view_apply_dns_config, name='apply_dns_config'), path('dns/manage_static_host/', view_manage_static_host, name='manage_static_host'), path('dns/manage_settings/', view_manage_dns_settings, name='manage_dns_settings'), + path('dns/manage_filter_list/', view_manage_filter_list, name='manage_filter_list'), path('peer/list/', view_wireguard_peer_list, name='wireguard_peer_list'), path('peer/sort/', view_wireguard_peer_sort, name='wireguard_peer_sort'), path('peer/manage/', view_wireguard_peer_manage, name='wireguard_peer_manage'),