mirror of
https://github.com/eduardogsilva/wireguard_webadmin.git
synced 2026-03-15 13:36:18 +00:00
add export configuration feature for Caddy
This commit is contained in:
145
app_gateway/caddy_config_export.py
Normal file
145
app_gateway/caddy_config_export.py
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
from app_gateway.models import (
|
||||||
|
AccessPolicy, Application, ApplicationPolicy
|
||||||
|
)
|
||||||
|
from gatekeeper.models import (
|
||||||
|
AuthMethod, GatekeeperGroup, GatekeeperIPAddress, GatekeeperUser
|
||||||
|
)
|
||||||
|
|
||||||
|
RESERVED_APP_NAME = 'wireguard_webadmin'
|
||||||
|
|
||||||
|
POLICY_TYPE_MAP = {
|
||||||
|
'public': 'bypass',
|
||||||
|
'protected': 'protected',
|
||||||
|
'deny': 'deny',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def build_applications_data():
|
||||||
|
applications = Application.objects.exclude(name=RESERVED_APP_NAME).prefetch_related('hosts')
|
||||||
|
entries = []
|
||||||
|
for app in applications:
|
||||||
|
entry = {
|
||||||
|
'id': app.name,
|
||||||
|
'name': app.display_name or app.name,
|
||||||
|
'hosts': list(app.hosts.values_list('hostname', flat=True)),
|
||||||
|
'upstream': app.upstream,
|
||||||
|
}
|
||||||
|
entries.append(entry)
|
||||||
|
return {'entries': entries}
|
||||||
|
|
||||||
|
|
||||||
|
def _build_auth_method_entry(method):
|
||||||
|
entry = {'type': method.auth_type}
|
||||||
|
|
||||||
|
if method.auth_type == 'totp':
|
||||||
|
entry['totp_secret'] = method.totp_secret
|
||||||
|
entry['totp_before_auth'] = method.totp_before_auth
|
||||||
|
|
||||||
|
elif method.auth_type == 'oidc':
|
||||||
|
entry['provider'] = method.oidc_provider
|
||||||
|
entry['client_id'] = method.oidc_client_id
|
||||||
|
entry['client_secret'] = method.oidc_client_secret
|
||||||
|
entry['allowed_domains'] = list(
|
||||||
|
method.allowed_domains.values_list('domain', flat=True)
|
||||||
|
)
|
||||||
|
entry['allowed_emails'] = list(
|
||||||
|
method.allowed_emails.values_list('email', flat=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
elif method.auth_type == 'ip_address':
|
||||||
|
rules = []
|
||||||
|
for ip_entry in GatekeeperIPAddress.objects.filter(auth_method=method):
|
||||||
|
rules.append({
|
||||||
|
'address': str(ip_entry.address),
|
||||||
|
'prefix_length': ip_entry.prefix_length,
|
||||||
|
'action': ip_entry.action,
|
||||||
|
'description': ip_entry.description,
|
||||||
|
})
|
||||||
|
entry['rules'] = rules
|
||||||
|
|
||||||
|
return entry
|
||||||
|
|
||||||
|
|
||||||
|
def build_auth_policies_data():
|
||||||
|
auth_methods = {}
|
||||||
|
for method in AuthMethod.objects.all():
|
||||||
|
auth_methods[method.name] = _build_auth_method_entry(method)
|
||||||
|
|
||||||
|
groups = {}
|
||||||
|
for group in GatekeeperGroup.objects.prefetch_related('users'):
|
||||||
|
groups[group.name] = {
|
||||||
|
'users': list(group.users.values_list('username', flat=True)),
|
||||||
|
}
|
||||||
|
|
||||||
|
users = {}
|
||||||
|
for user in GatekeeperUser.objects.all():
|
||||||
|
users[user.username] = {
|
||||||
|
'email': user.email,
|
||||||
|
'password_hash': user.password_hash or '',
|
||||||
|
'totp_secret': user.totp_secret,
|
||||||
|
}
|
||||||
|
|
||||||
|
policies = {}
|
||||||
|
for policy in AccessPolicy.objects.prefetch_related('groups', 'methods'):
|
||||||
|
policies[policy.name] = {
|
||||||
|
'policy_type': POLICY_TYPE_MAP.get(policy.policy_type, policy.policy_type),
|
||||||
|
'groups': list(policy.groups.values_list('name', flat=True)),
|
||||||
|
'methods': list(policy.methods.values_list('name', flat=True)),
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
'auth_methods': auth_methods,
|
||||||
|
'groups': groups,
|
||||||
|
'users': users,
|
||||||
|
'policies': policies,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def build_routes_data():
|
||||||
|
applications = (
|
||||||
|
Application.objects
|
||||||
|
.exclude(name=RESERVED_APP_NAME)
|
||||||
|
.prefetch_related('routes__policy')
|
||||||
|
)
|
||||||
|
entries = {}
|
||||||
|
for app in applications:
|
||||||
|
try:
|
||||||
|
app_policy = ApplicationPolicy.objects.get(application=app)
|
||||||
|
default_policy = app_policy.default_policy.name
|
||||||
|
except ApplicationPolicy.DoesNotExist:
|
||||||
|
default_policy = None
|
||||||
|
|
||||||
|
routes = []
|
||||||
|
for route in app.routes.all().order_by('order', 'path_prefix'):
|
||||||
|
routes.append({
|
||||||
|
'id': route.name,
|
||||||
|
'path_prefix': route.path_prefix,
|
||||||
|
'policy': route.policy.name,
|
||||||
|
})
|
||||||
|
|
||||||
|
entry = {'routes': routes}
|
||||||
|
if default_policy:
|
||||||
|
entry['default_policy'] = default_policy
|
||||||
|
|
||||||
|
entries[app.name] = entry
|
||||||
|
|
||||||
|
return {'entries': entries}
|
||||||
|
|
||||||
|
|
||||||
|
def export_caddy_config(output_dir):
|
||||||
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
|
|
||||||
|
file_map = {
|
||||||
|
'applications.json': build_applications_data,
|
||||||
|
'auth_policies.json': build_auth_policies_data,
|
||||||
|
'routes.json': build_routes_data,
|
||||||
|
}
|
||||||
|
|
||||||
|
for filename, builder in file_map.items():
|
||||||
|
filepath = os.path.join(output_dir, filename)
|
||||||
|
with open(filepath, 'w', encoding='utf-8') as output_file:
|
||||||
|
json.dump(builder(), output_file, indent=2)
|
||||||
|
output_file.write('\n')
|
||||||
@@ -27,4 +27,7 @@ urlpatterns = [
|
|||||||
# Application Routes
|
# Application Routes
|
||||||
path('route/manage/', views.view_manage_application_route, name='manage_application_route'),
|
path('route/manage/', views.view_manage_application_route, name='manage_application_route'),
|
||||||
path('route/delete/', views.view_delete_application_route, name='delete_application_route'),
|
path('route/delete/', views.view_delete_application_route, name='delete_application_route'),
|
||||||
|
|
||||||
|
# Config Export
|
||||||
|
path('export/caddy/', views.view_export_caddy_config, name='export_caddy_config'),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.db.models import ProtectedError
|
from django.db.models import ProtectedError
|
||||||
from django.shortcuts import render, get_object_or_404, redirect
|
from django.shortcuts import render, get_object_or_404, redirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
from django.views.decorators.http import require_POST
|
||||||
|
|
||||||
|
from app_gateway.caddy_config_export import export_caddy_config
|
||||||
from app_gateway.forms import (
|
from app_gateway.forms import (
|
||||||
ApplicationForm, ApplicationHostForm, AccessPolicyForm,
|
ApplicationForm, ApplicationHostForm, AccessPolicyForm,
|
||||||
ApplicationPolicyForm, ApplicationRouteForm
|
ApplicationPolicyForm, ApplicationRouteForm
|
||||||
@@ -418,3 +423,26 @@ def view_delete_application_route(request):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return render(request, 'generic_delete_confirmation.html', context)
|
return render(request, 'generic_delete_confirmation.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@require_POST
|
||||||
|
def view_export_caddy_config(request):
|
||||||
|
if not UserAcl.objects.filter(user=request.user).filter(user_level__gte=50).exists():
|
||||||
|
return render(request, 'access_denied.html', {'page_title': _('Access Denied')})
|
||||||
|
|
||||||
|
if settings.CADDY_ENABLED:
|
||||||
|
output_dir = '/caddy_json_export/'
|
||||||
|
else:
|
||||||
|
output_dir = os.path.join(settings.BASE_DIR, 'containers', 'caddy', 'config_files')
|
||||||
|
|
||||||
|
export_caddy_config(output_dir)
|
||||||
|
|
||||||
|
redirect_url = reverse('app_gateway_list') + '?tab=applications'
|
||||||
|
|
||||||
|
if settings.CADDY_ENABLED:
|
||||||
|
messages.success(request, _('Configuration exported successfully.'))
|
||||||
|
else:
|
||||||
|
messages.error(request, _('Caddy is not active. Configuration files were exported for debugging purposes.'))
|
||||||
|
|
||||||
|
return redirect(redirect_url)
|
||||||
|
|||||||
@@ -36,6 +36,12 @@
|
|||||||
</div>
|
</div>
|
||||||
{% if active_tab == 'applications' %}
|
{% if active_tab == 'applications' %}
|
||||||
<div>
|
<div>
|
||||||
|
<form method="post" action="{% url 'export_caddy_config' %}" class="d-inline">
|
||||||
|
{% csrf_token %}
|
||||||
|
<button type="submit" class="btn btn-outline-secondary">
|
||||||
|
<i class="fas fa-file-export"></i> {% trans 'Export Configuration' %}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
<a href="{% url 'manage_application' %}" class="btn btn-outline-primary">
|
<a href="{% url 'manage_application' %}" class="btn btn-outline-primary">
|
||||||
<i class="fas fa-plus"></i> {% trans 'Add Application' %}
|
<i class="fas fa-plus"></i> {% trans 'Add Application' %}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
Reference in New Issue
Block a user