diff --git a/wireguard_tools/__init__.py b/wireguard_tools/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/wireguard_tools/admin.py b/wireguard_tools/admin.py
new file mode 100644
index 0000000..8c38f3f
--- /dev/null
+++ b/wireguard_tools/admin.py
@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.
diff --git a/wireguard_tools/apps.py b/wireguard_tools/apps.py
new file mode 100644
index 0000000..5a7f7ef
--- /dev/null
+++ b/wireguard_tools/apps.py
@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class WireguardToolsConfig(AppConfig):
+ default_auto_field = 'django.db.models.BigAutoField'
+ name = 'wireguard_tools'
diff --git a/wireguard_tools/migrations/__init__.py b/wireguard_tools/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/wireguard_tools/models.py b/wireguard_tools/models.py
new file mode 100644
index 0000000..71a8362
--- /dev/null
+++ b/wireguard_tools/models.py
@@ -0,0 +1,3 @@
+from django.db import models
+
+# Create your models here.
diff --git a/wireguard_tools/tests.py b/wireguard_tools/tests.py
new file mode 100644
index 0000000..7ce503c
--- /dev/null
+++ b/wireguard_tools/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/wireguard_tools/views.py b/wireguard_tools/views.py
new file mode 100644
index 0000000..707b307
--- /dev/null
+++ b/wireguard_tools/views.py
@@ -0,0 +1,143 @@
+import os
+import re
+import qrcode
+import subprocess
+from django.http import HttpResponse
+from django.shortcuts import redirect, get_object_or_404, render
+from user_manager.models import UserAcl
+from wireguard.models import WireGuardInstance, Peer, PeerAllowedIP
+from django.contrib.auth.decorators import login_required
+from django.contrib import messages
+from io import BytesIO
+
+
+def clean_command_field(command_field):
+ cleaned_field = re.sub(r'[\r\n]+', '; ', command_field)
+ cleaned_field = re.sub(r'[\x00-\x1F\x7F]+', '', cleaned_field)
+ return cleaned_field
+
+
+def generate_peer_config(peer_uuid):
+ peer = get_object_or_404(Peer, uuid=peer_uuid)
+ wg_instance = peer.wireguard_instance
+
+ allowed_ips = PeerAllowedIP.objects.filter(peer=peer).order_by('priority')
+ allowed_ips_line = ", ".join([f"{ip.allowed_ip}/{ip.netmask}" for ip in allowed_ips])
+
+ config_lines = [
+ "[Interface]",
+ f"PrivateKey = {peer.private_key}" if peer.private_key else "",
+ f"Address = {wg_instance.address}/{wg_instance.netmask}",
+ f"DNS = 8.8.8.8", # Sorry, it's hardcoded for now, I will fix it later
+ "\n[Peer]",
+ f"PublicKey = {wg_instance.public_key}",
+ f"Endpoint = {wg_instance.hostname}:{wg_instance.listen_port}",
+ f"AllowedIPs = {allowed_ips_line}", # Usar os AllowedIPs do banco de dados
+ f"PresharedKey = {peer.pre_shared_key}" if peer.pre_shared_key else "",
+ f"PersistentKeepalive = {peer.persistent_keepalive}",
+ ]
+ return "\n".join(config_lines)
+
+
+@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'})
+ instances = WireGuardInstance.objects.all()
+ base_dir = "/etc/wireguard"
+
+ for instance in instances:
+ 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 ""
+
+ config_lines = [
+ "[Interface]",
+ f"PrivateKey = {instance.private_key}",
+ f"Address = {instance.address}/{instance.netmask}",
+ f"ListenPort = {instance.listen_port}",
+ f"PostUp = {post_up_processed}",
+ f"PostDown = {post_down_processed}",
+ f"PersistentKeepalive = {instance.persistent_keepalive}\n",
+ ]
+
+ peers = Peer.objects.filter(wireguard_instance=instance)
+ for peer in peers:
+ peer_lines = [
+ "[Peer]",
+ f"PublicKey = {peer.public_key}",
+ f"PresharedKey = {peer.pre_shared_key}" if peer.pre_shared_key else "",
+ f"PersistentKeepalive = {peer.persistent_keepalive}",
+ ]
+ allowed_ips = PeerAllowedIP.objects.filter(peer=peer).order_by('priority')
+ allowed_ips_line = "AllowedIPs = " + ", ".join([f"{ip.allowed_ip}/{ip.netmask}" for ip in allowed_ips])
+ peer_lines.append(allowed_ips_line)
+ config_lines.extend(peer_lines)
+ config_lines.append("")
+
+ config_content = "\n".join(config_lines)
+ config_path = os.path.join(base_dir, f"wg{instance.instance_id}.conf")
+
+ os.makedirs(base_dir, exist_ok=True)
+
+ with open(config_path, "w") as config_file:
+ config_file.write(config_content)
+ messages.success(request, "Export successful!|WireGuard configuration files have been exported to /etc/wireguard/. Don't forget to restart the interfaces.")
+ return redirect('/status/')
+
+
+@login_required
+def download_config_or_qrcode(request):
+ if not UserAcl.objects.filter(user=request.user).filter(user_level__gte=20).exists():
+ return render(request, 'access_denied.html', {'page_title': 'Access Denied'})
+ peer_uuid = request.GET.get('uuid')
+ format_type = request.GET.get('format', 'conf')
+
+ config_content = generate_peer_config(peer_uuid)
+
+ if format_type == 'qrcode':
+ qr = qrcode.QRCode(
+ version=1,
+ error_correction=qrcode.constants.ERROR_CORRECT_L,
+ box_size=10,
+ border=4,
+ )
+ qr.add_data(config_content)
+ qr.make(fit=True)
+ img = qr.make_image(fill_color="black", back_color="white")
+
+ response = HttpResponse(content_type="image/png")
+ img_io = BytesIO()
+ img.save(img_io)
+ img_io.seek(0)
+ response.write(img_io.getvalue())
+
+ else:
+ response = HttpResponse(config_content, content_type="text/plain")
+ response['Content-Disposition'] = f'attachment; filename="peer_{peer_uuid}.conf"'
+
+ return response
+
+
+@login_required
+def restart_wireguard_interfaces(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'})
+ config_dir = "/etc/wireguard"
+ interface_count = 0
+ for filename in os.listdir(config_dir):
+ if filename.endswith(".conf"):
+ interface_name = filename[:-5]
+ # Parar a interface
+ stop_command = f"wg-quick down {interface_name}"
+ subprocess.run(stop_command, shell=True, check=True)
+ start_command = f"wg-quick up {interface_name}"
+ subprocess.run(start_command, shell=True, check=True)
+ interface_count += 1
+ if interface_count == 1:
+ messages.success(request, "Interface restarted|The WireGuard interface has been restarted.")
+ elif interface_count > 1:
+ messages.success(request, "Interfaces restarted|" + str(interface_count) + " WireGuard interfaces have been restarted.")
+ else:
+ messages.warning(request, "No interfaces found|No WireGuard interfaces were found to restart.")
+ return redirect("/status/")
+
diff --git a/wireguard_webadmin/urls.py b/wireguard_webadmin/urls.py
index 891ee64..1d344df 100644
--- a/wireguard_webadmin/urls.py
+++ b/wireguard_webadmin/urls.py
@@ -21,6 +21,7 @@ from wireguard_peer.views import view_wireguard_peer_list, view_wireguard_peer_m
from console.views import view_console
from user_manager.views import view_user_list, view_manage_user
from accounts.views import view_create_first_user, view_login, view_logout
+from wireguard_tools.views import export_wireguard_configs, download_config_or_qrcode, restart_wireguard_interfaces
urlpatterns = [
@@ -33,6 +34,9 @@ urlpatterns = [
path('console/', view_console, name='console'),
path('user/list/', view_user_list, name='user_list'),
path('user/manage/', view_manage_user, name='manage_user'),
+ path('tools/export_wireguard_config/', 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'),
path('accounts/create_first_user/', view_create_first_user, name='create_first_user'),
path('accounts/login/', view_login, name='login'),