From 2945be9ffa3cfeb6bb457537e2793fe79c5ee8c5 Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Tue, 3 Feb 2026 16:50:29 -0300 Subject: [PATCH] improve export view --- ...llsettings_instance_to_execute_firewall.py | 20 +++++ firewall/models.py | 1 + wireguard_tools/views.py | 86 ++++++++++++------- wireguard_webadmin/urls.py | 4 +- 4 files changed, 80 insertions(+), 31 deletions(-) create mode 100644 firewall/migrations/0016_firewallsettings_instance_to_execute_firewall.py diff --git a/firewall/migrations/0016_firewallsettings_instance_to_execute_firewall.py b/firewall/migrations/0016_firewallsettings_instance_to_execute_firewall.py new file mode 100644 index 0000000..7ddb5c4 --- /dev/null +++ b/firewall/migrations/0016_firewallsettings_instance_to_execute_firewall.py @@ -0,0 +1,20 @@ +# Generated by Django 5.2.9 on 2026-02-03 19:30 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('firewall', '0015_alter_firewallsettings_default_forward_policy_and_more'), + ('wireguard', '0032_remove_peer_enabled_by_schedule'), + ] + + operations = [ + migrations.AddField( + model_name='firewallsettings', + name='instance_to_execute_firewall', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='wireguard.wireguardinstance'), + ), + ] diff --git a/firewall/models.py b/firewall/models.py index 2066d54..0f19536 100644 --- a/firewall/models.py +++ b/firewall/models.py @@ -79,6 +79,7 @@ class FirewallSettings(models.Model): wan_interface = models.CharField(max_length=12, default='eth0') pending_changes = models.BooleanField(default=False) last_firewall_reset = models.DateTimeField(blank=True, null=True) + instance_to_execute_firewall = models.ForeignKey(WireGuardInstance, on_delete=models.SET_NULL, blank=True, null=True) created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) diff --git a/wireguard_tools/views.py b/wireguard_tools/views.py index ee8d955..9f943b2 100644 --- a/wireguard_tools/views.py +++ b/wireguard_tools/views.py @@ -14,7 +14,7 @@ from django.utils import timezone from django.utils.translation import gettext_lazy as _ from cluster.models import ClusterSettings, Worker -from firewall.models import RedirectRule +from firewall.models import RedirectRule, FirewallSettings from firewall.tools import export_user_firewall, generate_firewall_footer, generate_firewall_header, \ generate_port_forward_firewall, generate_redirect_dns_rules, generate_route_policy_rules from user_manager.models import UserAcl @@ -76,43 +76,61 @@ def export_firewall_configuration(): return -@login_required -def export_wireguard_configs(request): - if not UserAcl.objects.filter(user=request.user).filter(user_level__gte=30).exists(): - return render(request, 'access_denied.html', {'page_title': 'Access Denied'}) +def set_instance_to_include_firewall(): + firewall_settings, firewall_settings_created = FirewallSettings.objects.get_or_create(name='global') + if firewall_settings.instance_to_execute_firewall: + force_export_all_instances = False + instance_to_execute_firewall = firewall_settings.instance_to_execute_firewall + else: + force_export_all_instances = True + instance_to_execute_firewall = WireGuardInstance.objects.order_by('instance_id').first() + if instance_to_execute_firewall: + firewall_settings.instance_to_execute_firewall = instance_to_execute_firewall + firewall_settings.save() + return instance_to_execute_firewall, force_export_all_instances + + +def export_wireguard_configuration(instance_only: WireGuardInstance = None): cluster_settings = ClusterSettings.objects.filter(name='cluster_settings', enabled=True).first() if cluster_settings: if WireGuardInstance.objects.filter(pending_changes=True).exists(): cluster_settings.config_version += 1 cluster_settings.save() - instances = WireGuardInstance.objects.all() + cleanup_orphaned = True + instance_to_execute_firewall, force_export_all_instances = set_instance_to_include_firewall() + + if instance_only and not force_export_all_instances: + instances = WireGuardInstance.objects.filter(id=instance_only.id) + cleanup_orphaned = False + else: + instances = WireGuardInstance.objects.all() + base_dir = "/etc/wireguard" os.makedirs(base_dir, exist_ok=True) - export_firewall_configuration() - firewall_inserted = False - active_instance_conf_set = {f"wg{instance.instance_id}.conf" for instance in instances} - for old_conf_file in glob.glob(os.path.join(base_dir, "wg[0-9]*.conf")): - filename = os.path.basename(old_conf_file) - if filename not in active_instance_conf_set: - logging.info("Removing abandoned WireGuard config: %s", filename) - try: - os.remove(old_conf_file) - except FileNotFoundError: - pass - except IsADirectoryError: - continue - except PermissionError: - continue + if cleanup_orphaned: + active_instance_conf_set = {f"wg{instance.instance_id}.conf" for instance in instances} + for old_conf_file in glob.glob(os.path.join(base_dir, "wg[0-9]*.conf")): + filename = os.path.basename(old_conf_file) + if filename not in active_instance_conf_set: + logging.info("Removing abandoned WireGuard config: %s", filename) + try: + os.remove(old_conf_file) + except FileNotFoundError: + pass + except IsADirectoryError: + continue + except PermissionError: + continue for instance in instances: if instance.legacy_firewall: post_up_processed = clean_command_field(instance.post_up) if instance.post_up else "" post_down_processed = clean_command_field(instance.post_down) if instance.post_down else "" - + if post_up_processed: post_up_processed += '; ' if post_down_processed: @@ -127,27 +145,26 @@ def export_wireguard_configs(request): if peer_allowed_ip_address: rule_destination = peer_allowed_ip_address.allowed_ip if rule_destination: - rule_text_up = f"iptables -t nat -A PREROUTING -p {redirect_rule.protocol} -d wireguard-webadmin --dport {redirect_rule.port} -j DNAT --to-dest {rule_destination}:{redirect_rule.port} ; " + rule_text_up = f"iptables -t nat -A PREROUTING -p {redirect_rule.protocol} -d wireguard-webadmin --dport {redirect_rule.port} -j DNAT --to-dest {rule_destination}:{redirect_rule.port} ; " rule_text_down = f"iptables -t nat -D PREROUTING -p {redirect_rule.protocol} -d wireguard-webadmin --dport {redirect_rule.port} -j DNAT --to-dest {rule_destination}:{redirect_rule.port} ; " if redirect_rule.add_forward_rule: - rule_text_up += f"iptables -A FORWARD -d {rule_destination} -p {redirect_rule.protocol} --dport {redirect_rule.port} -j ACCEPT ; " + rule_text_up += f"iptables -A FORWARD -d {rule_destination} -p {redirect_rule.protocol} --dport {redirect_rule.port} -j ACCEPT ; " rule_text_down += f"iptables -D FORWARD -d {rule_destination} -p {redirect_rule.protocol} --dport {redirect_rule.port} -j ACCEPT ; " if redirect_rule.masquerade_source: - rule_text_up += f"iptables -t nat -A POSTROUTING -d {rule_destination} -p {redirect_rule.protocol} --dport {redirect_rule.port} -j MASQUERADE ; " + rule_text_up += f"iptables -t nat -A POSTROUTING -d {rule_destination} -p {redirect_rule.protocol} --dport {redirect_rule.port} -j MASQUERADE ; " rule_text_down += f"iptables -t nat -D POSTROUTING -d {rule_destination} -p {redirect_rule.protocol} --dport {redirect_rule.port} -j MASQUERADE ; " post_up_processed += rule_text_up post_down_processed += rule_text_down - + pass else: post_down_processed = '' - - if not firewall_inserted: + + if not firewall_inserted and instance == instance_to_execute_firewall: post_up_processed = '/etc/wireguard/wg-firewall.sh' firewall_inserted = True else: post_up_processed = '' - config_lines = [ "[Interface]", @@ -177,6 +194,17 @@ def export_wireguard_configs(request): with open(config_path, "w") as config_file: config_file.write(config_content) + return + + +@login_required +def view_export_wireguard_configs(request): + if not UserAcl.objects.filter(user=request.user).filter(user_level__gte=30).exists(): + return render(request, 'access_denied.html', {'page_title': 'Access Denied'}) + + export_firewall_configuration() + export_wireguard_configuration() + if request.GET.get('action') == 'update_and_restart' or request.GET.get('action') == 'update_and_reload': messages.success(request, _("Export successful!|WireGuard configuration files have been exported to /etc/wireguard/.")) else: diff --git a/wireguard_webadmin/urls.py b/wireguard_webadmin/urls.py index 2f2af11..4b81286 100644 --- a/wireguard_webadmin/urls.py +++ b/wireguard_webadmin/urls.py @@ -43,7 +43,7 @@ from wireguard.views import view_apply_db_patches, view_wireguard_manage_instanc from wireguard_peer.views import view_manage_ip_address, view_wireguard_peer_list, view_wireguard_peer_manage, \ view_wireguard_peer_sort, view_apply_route_template, view_wireguard_peer_create, view_wireguard_peer_edit_field, \ view_wireguard_peer_suspend, view_wireguard_peer_schedule_profile -from wireguard_tools.views import download_config_or_qrcode, export_wireguard_configs, restart_wireguard_interfaces +from wireguard_tools.views import download_config_or_qrcode, view_export_wireguard_configs, restart_wireguard_interfaces urlpatterns = [ path('admin/', admin.site.urls), @@ -71,7 +71,7 @@ urlpatterns = [ path('user/manage/', view_manage_user, name='manage_user'), path('user/peer-group/list/', view_peer_group_list, name='peer_group_list'), path('user/peer-group/manage/', view_peer_group_manage, name='peer_group_manage'), - path('tools/export_wireguard_config/', export_wireguard_configs, name='export_wireguard_configs'), + path('tools/export_wireguard_config/', view_export_wireguard_configs, name='export_wireguard_configs'), path('tools/download_peer_config/', download_config_or_qrcode, name='download_config_or_qrcode'), path('tools/restart_wireguard/', restart_wireguard_interfaces, name='restart_wireguard_interfaces'), path('server/manage/', view_wireguard_manage_instance, name='wireguard_manage_instance'),