mirror of
https://github.com/eduardogsilva/wireguard_webadmin.git
synced 2026-01-10 01:46:17 +00:00
add WireGuard status processing and caching functionality
This commit is contained in:
@@ -1,3 +1,10 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from .models import WireguardStatusCache
|
||||||
|
|
||||||
|
|
||||||
# Register your models here.
|
# Register your models here.
|
||||||
|
class WireguardStatusCacheAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('cache_type', 'processing_time_ms', 'created', 'updated')
|
||||||
|
readonly_fields = ('uuid', 'created', 'updated')
|
||||||
|
admin.site.register(WireguardStatusCache, WireguardStatusCacheAdmin)
|
||||||
20
api/migrations/0002_alter_wireguardstatuscache_uuid.py
Normal file
20
api/migrations/0002_alter_wireguardstatuscache_uuid.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Generated by Django 5.2.9 on 2026-01-07 13:28
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('api', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='wireguardstatuscache',
|
||||||
|
name='uuid',
|
||||||
|
field=models.UUIDField(default=uuid.uuid4, editable=False, unique=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
|
import uuid
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class WireguardStatusCache(models.Model):
|
class WireguardStatusCache(models.Model):
|
||||||
cache_type = models.CharField(choices=(('master', 'Master'), ('cluster', 'Cluster')), max_length=16)
|
cache_type = models.CharField(choices=(('master', 'Master'), ('cluster', 'Cluster')), max_length=16)
|
||||||
data = models.JSONField()
|
data = models.JSONField()
|
||||||
processing_time_ms = models.PositiveIntegerField()
|
processing_time_ms = models.PositiveIntegerField()
|
||||||
|
|
||||||
uuid = models.UUIDField(unique=True)
|
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
updated = models.DateTimeField(auto_now=True)
|
updated = models.DateTimeField(auto_now=True)
|
||||||
143
api/views.py
143
api/views.py
@@ -2,6 +2,7 @@ import base64
|
|||||||
import datetime
|
import datetime
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import pytz
|
import pytz
|
||||||
@@ -17,6 +18,7 @@ from django.shortcuts import get_object_or_404, redirect
|
|||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.views.decorators.http import require_http_methods
|
from django.views.decorators.http import require_http_methods
|
||||||
|
|
||||||
|
from api.models import WireguardStatusCache
|
||||||
from user_manager.models import AuthenticationToken, UserAcl
|
from user_manager.models import AuthenticationToken, UserAcl
|
||||||
from vpn_invite.models import InviteSettings, PeerInvite
|
from vpn_invite.models import InviteSettings, PeerInvite
|
||||||
from wgwadmlibrary.tools import create_peer_invite, get_peer_invite_data, send_email, user_allowed_peers, \
|
from wgwadmlibrary.tools import create_peer_invite, get_peer_invite_data, send_email, user_allowed_peers, \
|
||||||
@@ -208,12 +210,153 @@ def api_instance_info(request):
|
|||||||
}
|
}
|
||||||
return JsonResponse(data)
|
return JsonResponse(data)
|
||||||
|
|
||||||
|
|
||||||
|
def func_process_wireguard_status():
|
||||||
|
# Query WireGuard status from the system and construct the data dictionary
|
||||||
|
commands = {
|
||||||
|
'latest-handshakes': "wg show all latest-handshakes | expand | tr -s ' '",
|
||||||
|
'allowed-ips': "wg show all allowed-ips | expand | tr -s ' '",
|
||||||
|
'transfer': "wg show all transfer | expand | tr -s ' '",
|
||||||
|
'endpoints': "wg show all endpoints | expand | tr -s ' '",
|
||||||
|
}
|
||||||
|
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
for key, command in commands.items():
|
||||||
|
process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||||
|
stdout, stderr = process.communicate()
|
||||||
|
|
||||||
|
if process.returncode != 0:
|
||||||
|
return JsonResponse({'error': stderr}, status=400)
|
||||||
|
|
||||||
|
current_interface = None
|
||||||
|
for line in stdout.strip().split('\n'):
|
||||||
|
parts = line.split()
|
||||||
|
if len(parts) >= 3:
|
||||||
|
interface, peer, value = parts[0], parts[1], " ".join(parts[2:])
|
||||||
|
current_interface = interface
|
||||||
|
elif len(parts) == 2 and current_interface:
|
||||||
|
peer, value = parts
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if interface not in data:
|
||||||
|
data[interface] = {}
|
||||||
|
|
||||||
|
if peer not in data[interface]:
|
||||||
|
data[interface][peer] = {
|
||||||
|
'allowed-ips': [],
|
||||||
|
'latest-handshakes': '',
|
||||||
|
'transfer': {'tx': 0, 'rx': 0},
|
||||||
|
'endpoints': '',
|
||||||
|
}
|
||||||
|
|
||||||
|
if key == 'allowed-ips':
|
||||||
|
data[interface][peer]['allowed-ips'].append(value)
|
||||||
|
elif key == 'transfer':
|
||||||
|
rx, tx = value.split()[-2:]
|
||||||
|
data[interface][peer]['transfer'] = {'tx': int(tx), 'rx': int(rx)}
|
||||||
|
elif key == 'endpoints':
|
||||||
|
data[interface][peer]['endpoints'] = value
|
||||||
|
else:
|
||||||
|
data[interface][peer][key] = value
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def func_apply_enhanced_filter(data: dict, user_acl: UserAcl):
|
||||||
|
# Remove peers and instances that are not allowed for the user
|
||||||
|
if user_acl.enable_enhanced_filter:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def func_get_wireguard_status():
|
||||||
|
if settings.WIREGUARD_STATUS_CACHE_ENABLED:
|
||||||
|
cache_entry = WireguardStatusCache.objects.filter(cache_type='master').order_by('-created').first()
|
||||||
|
if cache_entry:
|
||||||
|
data = cache_entry.data
|
||||||
|
data['cache_information'] = {
|
||||||
|
'processing_time_ms': cache_entry.processing_time_ms,
|
||||||
|
'created': cache_entry.created.isoformat(),
|
||||||
|
'cache_type': cache_entry.cache_type,
|
||||||
|
'cache_hit': True,
|
||||||
|
'cache_enabled': True,
|
||||||
|
'cache_uuid': str(cache_entry.uuid),
|
||||||
|
}
|
||||||
|
data['status'] = 'success'
|
||||||
|
data['message'] = 'WireGuard status retrieved from cache'
|
||||||
|
else:
|
||||||
|
data = {
|
||||||
|
'status': 'error',
|
||||||
|
'message': 'No cache entry found',
|
||||||
|
'cache_information': {
|
||||||
|
'cache_hit': False,
|
||||||
|
'cache_enabled': True,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
data = func_process_wireguard_status()
|
||||||
|
data['cache_information'] = {
|
||||||
|
'cache_hit': False,
|
||||||
|
'cache_enabled': False,
|
||||||
|
}
|
||||||
|
data['status'] = 'success'
|
||||||
|
data['message'] = 'WireGuard status retrieved without cache'
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def cron_refresh_wireguard_status_cache(request):
|
||||||
|
data = {'status': 'success'}
|
||||||
|
WireguardStatusCache.objects.filter(created__lt=timezone.now() - timezone.timedelta(seconds=settings.WIREGUARD_STATUS_CACHE_MAX_AGE)).delete()
|
||||||
|
|
||||||
|
if not settings.WIREGUARD_STATUS_CACHE_ENABLED:
|
||||||
|
return JsonResponse(data)
|
||||||
|
start_time = time.monotonic()
|
||||||
|
data = func_process_wireguard_status()
|
||||||
|
end_time = time.monotonic()
|
||||||
|
processing_time_ms = int((end_time - start_time) * 1000)
|
||||||
|
WireguardStatusCache.objects.create(data=data, processing_time_ms=processing_time_ms, cache_type='master')
|
||||||
|
return JsonResponse(data)
|
||||||
|
|
||||||
|
|
||||||
@require_http_methods(["GET"])
|
@require_http_methods(["GET"])
|
||||||
def wireguard_status(request):
|
def wireguard_status(request):
|
||||||
user_acl = None
|
user_acl = None
|
||||||
enhanced_filter = False
|
enhanced_filter = False
|
||||||
filter_peer_list = []
|
filter_peer_list = []
|
||||||
|
|
||||||
|
if request.user.is_authenticated:
|
||||||
|
user_acl = get_object_or_404(UserAcl, user=request.user)
|
||||||
|
if user_acl.enable_enhanced_filter and user_acl.peer_groups.count() > 0:
|
||||||
|
enhanced_filter = True
|
||||||
|
elif request.GET.get('key'):
|
||||||
|
api_key = get_api_key('api')
|
||||||
|
if api_key and api_key == request.GET.get('key'):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return HttpResponseForbidden()
|
||||||
|
elif request.GET.get('rrdkey'):
|
||||||
|
api_key = get_api_key('rrdkey')
|
||||||
|
if api_key and api_key == request.GET.get('rrdkey'):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return HttpResponseForbidden()
|
||||||
|
else:
|
||||||
|
return HttpResponseForbidden()
|
||||||
|
|
||||||
|
data = func_get_wireguard_status()
|
||||||
|
return JsonResponse(data)
|
||||||
|
|
||||||
|
|
||||||
|
@require_http_methods(["GET"])
|
||||||
|
def legacy_wireguard_status(request):
|
||||||
|
user_acl = None
|
||||||
|
enhanced_filter = False
|
||||||
|
filter_peer_list = []
|
||||||
|
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated:
|
||||||
user_acl = get_object_or_404(UserAcl, user=request.user)
|
user_acl = get_object_or_404(UserAcl, user=request.user)
|
||||||
if user_acl.enable_enhanced_filter and user_acl.peer_groups.count() > 0:
|
if user_acl.enable_enhanced_filter and user_acl.peer_groups.count() > 0:
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
* * * * * root /usr/bin/curl -s http://wireguard-webadmin:8000/api/cron_check_updates/ >> /var/log/cron.log 2>&1
|
* * * * * root /usr/bin/curl -s http://wireguard-webadmin:8000/api/cron_check_updates/ >> /var/log/cron.log 2>&1
|
||||||
*/10 * * * * root /usr/bin/curl -s http://wireguard-webadmin:8000/api/cron_update_peer_latest_handshake/ >> /var/log/cron.log 2>&1
|
*/10 * * * * root /usr/bin/curl -s http://wireguard-webadmin:8000/api/cron_update_peer_latest_handshake/ >> /var/log/cron.log 2>&1
|
||||||
|
* * * * * root /usr/bin/curl -s http://wireguard-webadmin:8000/api/cron_refresh_wireguard_status_cache/ >> /var/log/cron.log 2>&1
|
||||||
|
|||||||
@@ -155,6 +155,8 @@ STATICFILES_DIRS = [
|
|||||||
BASE_DIR / "static_files",
|
BASE_DIR / "static_files",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
WIREGUARD_STATUS_CACHE_ENABLED = True
|
||||||
|
WIREGUARD_STATUS_CACHE_MAX_AGE = 600
|
||||||
# Default primary key field type
|
# Default primary key field type
|
||||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
|
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
|
||||||
DNS_CONFIG_FILE = '/etc/dnsmasq/wireguard_webadmin_dns.conf'
|
DNS_CONFIG_FILE = '/etc/dnsmasq/wireguard_webadmin_dns.conf'
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ from django.urls import path
|
|||||||
from accounts.views import view_create_first_user, view_login, view_logout
|
from accounts.views import view_create_first_user, view_login, view_logout
|
||||||
from api.views import api_instance_info, api_peer_invite, api_peer_list, cron_check_updates, \
|
from api.views import api_instance_info, api_peer_invite, api_peer_list, cron_check_updates, \
|
||||||
cron_update_peer_latest_handshake, peer_info, routerfleet_authenticate_session, routerfleet_get_user_token, \
|
cron_update_peer_latest_handshake, peer_info, routerfleet_authenticate_session, routerfleet_get_user_token, \
|
||||||
wireguard_status
|
wireguard_status, cron_refresh_wireguard_status_cache
|
||||||
from cluster.cluster_api import api_cluster_status, api_get_worker_config_files, api_get_worker_dnsmasq_config, \
|
from cluster.cluster_api import api_cluster_status, api_get_worker_config_files, api_get_worker_dnsmasq_config, \
|
||||||
api_worker_ping
|
api_worker_ping
|
||||||
from cluster.views import cluster_main, cluster_settings, worker_manage
|
from cluster.views import cluster_main, cluster_settings, worker_manage
|
||||||
@@ -75,6 +75,7 @@ urlpatterns = [
|
|||||||
path('api/instance_info/', api_instance_info, name='api_instance_info'),
|
path('api/instance_info/', api_instance_info, name='api_instance_info'),
|
||||||
path('api/peer_info/', peer_info, name='api_peer_info'),
|
path('api/peer_info/', peer_info, name='api_peer_info'),
|
||||||
path('api/peer_invite/', api_peer_invite, name='api_peer_invite'),
|
path('api/peer_invite/', api_peer_invite, name='api_peer_invite'),
|
||||||
|
path('api/cron_refresh_wireguard_status_cache/', cron_refresh_wireguard_status_cache, name='cron_refresh_wireguard_status_cache'),
|
||||||
path('api/cron_check_updates/', cron_check_updates, name='cron_check_updates'),
|
path('api/cron_check_updates/', cron_check_updates, name='cron_check_updates'),
|
||||||
path('api/cron_update_peer_latest_handshake/', cron_update_peer_latest_handshake, name='cron_update_peer_latest_handshake'),
|
path('api/cron_update_peer_latest_handshake/', cron_update_peer_latest_handshake, name='cron_update_peer_latest_handshake'),
|
||||||
path('api/cluster/status/', api_cluster_status, name='api_cluster_status'),
|
path('api/cluster/status/', api_cluster_status, name='api_cluster_status'),
|
||||||
|
|||||||
Reference in New Issue
Block a user