From 32931dfd1660fa0e8ffd90c47922751f45048540 Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Sat, 9 Mar 2024 16:02:48 -0300 Subject: [PATCH] Manage AllowedIPs for client config file --- firewall/tools.py | 4 +- templates/wireguard/welcome.html | 1 - .../wireguard/wireguard_manage_peer.html | 107 ++++++++++++++---- templates/wireguard/wireguard_peer_list.html | 41 +------ .../0020_peerallowedip_config_file.py | 18 +++ ...ve_peerallowedip_missing_from_wireguard.py | 17 +++ wireguard/models.py | 2 +- wireguard_peer/forms.py | 49 ++++---- wireguard_peer/views.py | 38 ++++--- wireguard_tools/views.py | 15 ++- wireguard_webadmin/settings.py | 2 +- 11 files changed, 188 insertions(+), 106 deletions(-) create mode 100644 wireguard/migrations/0020_peerallowedip_config_file.py create mode 100644 wireguard/migrations/0021_remove_peerallowedip_missing_from_wireguard.py diff --git a/firewall/tools.py b/firewall/tools.py index 5f9f5dc..c1daf63 100644 --- a/firewall/tools.py +++ b/firewall/tools.py @@ -6,7 +6,7 @@ from django.utils import timezone def get_peer_addresses(peers, include_networks): addresses = [] for peer in peers.all(): - peer_ips = peer.peerallowedip_set.all().order_by('priority') + peer_ips = peer.peerallowedip_set.filter(config_file='server').order_by('priority') if not include_networks: peer_ips = peer_ips.filter(priority=0) @@ -202,7 +202,7 @@ def generate_port_forward_firewall(): description = f" - {redirect_rule.description} " if redirect_rule.description else "" rule_destination = redirect_rule.ip_address if redirect_rule.peer: - peer_allowed_ip_address = PeerAllowedIP.objects.filter(peer=redirect_rule.peer, netmask=32, priority=0).first() + peer_allowed_ip_address = PeerAllowedIP.objects.filter(peer=redirect_rule.peer, config_file='server', netmask=32, priority=0).first() if peer_allowed_ip_address: rule_destination = peer_allowed_ip_address.allowed_ip if rule_destination: diff --git a/templates/wireguard/welcome.html b/templates/wireguard/welcome.html index 6c8347a..50e076d 100644 --- a/templates/wireguard/welcome.html +++ b/templates/wireguard/welcome.html @@ -9,7 +9,6 @@

Upcoming Enhancements

diff --git a/templates/wireguard/wireguard_manage_peer.html b/templates/wireguard/wireguard_manage_peer.html index aa364ac..7ace359 100644 --- a/templates/wireguard/wireguard_manage_peer.html +++ b/templates/wireguard/wireguard_manage_peer.html @@ -47,30 +47,91 @@
- - {% for ip_address in peer_ip_list %} -
-

+

+
+
+ + Add IP Address +
+ + {% for ip_address in peer_ip_list %} +
+

+ + + {{ ip_address}} + + + + +

+

+ {% if ip_address.priority == 0 %} + Main ip address + {% else %} + Priority: {{ ip_address.priority }} + {% endif %} +

+
+ {% endfor %} + +
+
+
+
+
+ + Add Client route +
+
+

+ + + 0.0.0.0/0, ::/0 + +

+

+ default route +

+
+ + + {% for ip_address in peer_client_ip_list %} +
+

+ + + {{ ip_address}} + +

+

+ {% if ip_address.priority == 0 %} + Main ip address + {% else %} + Priority: {{ ip_address.priority }} + {% endif %} +

+
+ {% endfor %} + +
+
+ - {% if ip_address.missing_from_wireguard %} - - - {{ ip_address}} - - {% else %} - - - {{ ip_address}} - - {% endif %} - - -

-

- Priority: {{ ip_address.priority }} -

-
- {% endfor %}
diff --git a/templates/wireguard/wireguard_peer_list.html b/templates/wireguard/wireguard_peer_list.html index 19b49d7..284836f 100644 --- a/templates/wireguard/wireguard_peer_list.html +++ b/templates/wireguard/wireguard_peer_list.html @@ -39,43 +39,14 @@ Latest Handshake:
Endpoints:
Allowed IPs: - {% for address in peer.peerallowedip_set.all %}{% if address.priority == 0 %} - {% if address.missing_from_wireguard %} - {{ address }} - {% else %} - {{ address }} - {% endif %} - {% endif %}{% endfor %} - - {% for address in peer.peerallowedip_set.all %}{% if address.priority >= 1 %} - {% if address.missing_from_wireguard %} - {{ address }} - {% else %} - {{ address }} - {% endif %} - {% endif %}{% endfor %} + {% for address in peer.peerallowedip_set.all %} + {% if address.priority == 0 and address.config_file == 'server' %}{{ address }}{% endif %} + {% endfor %} + {% for address in peer.peerallowedip_set.all %} + {% if address.priority >= 1 and address.config_file == 'server' %}{{ address }}{% endif %} + {% endfor %}

- - {% comment %} -

{% for address in peer.peerallowedip_set.all %}{% if address.priority == 0 %} - {% if address.missing_from_wireguard %} - {{ address }} - {% else %} - {{ address }} - {% endif %} - {% endif %}{% endfor %} - - {% for address in peer.peerallowedip_set.all %}{% if address.priority >= 1 %} - {% if address.missing_from_wireguard %} - {{ address }} - {% else %} - {{ address }} - {% endif %} - {% endif %}{% endfor %} -

- {% endcomment %} - diff --git a/wireguard/migrations/0020_peerallowedip_config_file.py b/wireguard/migrations/0020_peerallowedip_config_file.py new file mode 100644 index 0000000..1c35f7e --- /dev/null +++ b/wireguard/migrations/0020_peerallowedip_config_file.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-03-08 18:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('wireguard', '0019_alter_wireguardinstance_legacy_firewall'), + ] + + operations = [ + migrations.AddField( + model_name='peerallowedip', + name='config_file', + field=models.CharField(choices=[('server', 'Server Config'), ('client', 'Client config')], default='server', max_length=6), + ), + ] diff --git a/wireguard/migrations/0021_remove_peerallowedip_missing_from_wireguard.py b/wireguard/migrations/0021_remove_peerallowedip_missing_from_wireguard.py new file mode 100644 index 0000000..03bcff0 --- /dev/null +++ b/wireguard/migrations/0021_remove_peerallowedip_missing_from_wireguard.py @@ -0,0 +1,17 @@ +# Generated by Django 5.0.2 on 2024-03-08 18:49 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('wireguard', '0020_peerallowedip_config_file'), + ] + + operations = [ + migrations.RemoveField( + model_name='peerallowedip', + name='missing_from_wireguard', + ), + ] diff --git a/wireguard/models.py b/wireguard/models.py index fb4a579..447c686 100644 --- a/wireguard/models.py +++ b/wireguard/models.py @@ -111,7 +111,7 @@ class PeerAllowedIP(models.Model): priority = models.PositiveBigIntegerField(default=1) allowed_ip = models.GenericIPAddressField(protocol='IPv4') netmask = models.IntegerField(default=32, choices=NETMASK_CHOICES) - missing_from_wireguard = models.BooleanField(default=False) + config_file = models.CharField(max_length=6, choices=(('server', 'Server Config'), ('client', 'Client config')), default='server') created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) diff --git a/wireguard_peer/forms.py b/wireguard_peer/forms.py index 1213ac2..808ce50 100644 --- a/wireguard_peer/forms.py +++ b/wireguard_peer/forms.py @@ -18,8 +18,10 @@ class PeerForm(forms.ModelForm): class PeerAllowedIPForm(forms.ModelForm): def __init__(self, *args, **kwargs): current_peer = kwargs.pop('current_peer', None) + config_file = kwargs.pop('config_file', None) super().__init__(*args, **kwargs) self.current_peer = current_peer + self.config_file = config_file allowed_ip = forms.GenericIPAddressField(label='Allowed IP or Network', required=True) netmask = forms.ChoiceField(choices=NETMASK_CHOICES, label='Netmask', initial=24, required=True) @@ -33,29 +35,34 @@ class PeerAllowedIPForm(forms.ModelForm): if allowed_ip is None: raise forms.ValidationError("Please provide a valid IP address.") - wireguard_network = ipaddress.ip_network(f"{self.current_peer.wireguard_instance.address}/{self.current_peer.wireguard_instance.netmask}", strict=False) + if self.config_file == 'server': + wireguard_network = ipaddress.ip_network(f"{self.current_peer.wireguard_instance.address}/{self.current_peer.wireguard_instance.netmask}", strict=False) + if priority == 0: + zero_priority_ips_query = PeerAllowedIP.objects.filter(peer=self.current_peer, config_file='server', priority=0) + if self.instance: + zero_priority_ips_query = zero_priority_ips_query.exclude(uuid=self.instance.uuid) + if zero_priority_ips_query.exists(): + raise forms.ValidationError("A peer can have only one IP with priority zero.") - if priority == 0: - zero_priority_ips_query = PeerAllowedIP.objects.filter(peer=self.current_peer, priority=0) - if self.instance: - zero_priority_ips_query = zero_priority_ips_query.exclude(uuid=self.instance.uuid) - if zero_priority_ips_query.exists(): - raise forms.ValidationError("A peer can have only one IP with priority zero.") - - duplicated_ip = PeerAllowedIP.objects.filter(allowed_ip=allowed_ip) - if self.instance: - duplicated_ip = duplicated_ip.exclude(uuid=self.instance.uuid) - if duplicated_ip.exists(): - raise forms.ValidationError("This IP is already in use by another peer.") - if ipaddress.ip_address(allowed_ip) not in wireguard_network: - raise forms.ValidationError("The IP address does not belong to the Peer's WireGuard instance network range. Please check the IP address or change the priority.") - if str(netmask) != str(32): - raise forms.ValidationError("The netmask for priority 0 IP must be 32.") - if self.current_peer.wireguard_instance.address == allowed_ip: - raise forms.ValidationError("The IP address is the same as the Peer's WireGuard instance address.") + duplicated_ip = PeerAllowedIP.objects.filter(config_file='server', allowed_ip=allowed_ip) + if self.instance: + duplicated_ip = duplicated_ip.exclude(uuid=self.instance.uuid) + if duplicated_ip.exists(): + raise forms.ValidationError("This IP is already in use by another peer.") + if ipaddress.ip_address(allowed_ip) not in wireguard_network: + raise forms.ValidationError("The IP address does not belong to the Peer's WireGuard instance network range. Please check the IP address or change the priority.") + if str(netmask) != str(32): + raise forms.ValidationError("The netmask for priority 0 IP must be 32.") + if self.current_peer.wireguard_instance.address == allowed_ip: + raise forms.ValidationError("The IP address is the same as the Peer's WireGuard instance address.") + else: + if ipaddress.ip_address(allowed_ip) in wireguard_network: + raise forms.ValidationError("The IP address belongs to the Peer's WireGuard instance network range. Please check the IP address or change use priority 0 instead.") + elif self.config_file == 'client': + if priority < 1: + raise forms.ValidationError("Priority must be greater than or equal to 1") else: - if ipaddress.ip_address(allowed_ip) in wireguard_network: - raise forms.ValidationError("The IP address belongs to the Peer's WireGuard instance network range. Please check the IP address or change use priority 0 instead.") + raise forms.ValidationError('Invalid config file') class Meta: model = PeerAllowedIP diff --git a/wireguard_peer/views.py b/wireguard_peer/views.py index 1205cc2..a6b0d82 100644 --- a/wireguard_peer/views.py +++ b/wireguard_peer/views.py @@ -22,7 +22,7 @@ def generate_peer_default(wireguard_instance): # the code below can be an issue for larger networks, for now it's fine, but it should be optimized in the future used_ips = set(WireGuardInstance.objects.all().values_list('address', flat=True)) | \ - set(PeerAllowedIP.objects.filter(priority=0).values_list('allowed_ip', flat=True)) + set(PeerAllowedIP.objects.filter(config_file='server', priority=0).values_list('allowed_ip', flat=True)) free_ip_address = None for ip in network.hosts(): @@ -88,6 +88,7 @@ def view_wireguard_peer_manage(request): wireguard_instance=current_instance, ) PeerAllowedIP.objects.create( + config_file='server', peer=new_peer, allowed_ip=new_peer_data['allowed_ip'], priority=0, @@ -115,7 +116,8 @@ def view_wireguard_peer_manage(request): messages.warning(request, 'Error deleting peer|Invalid confirmation message. Type "delete" to confirm.') return redirect('/peer/manage/?peer=' + str(current_peer.uuid)) page_title = 'Update Peer ' - peer_ip_list = current_peer.peerallowedip_set.all().order_by('priority') + peer_ip_list = current_peer.peerallowedip_set.filter(config_file='server').order_by('priority') + peer_client_ip_list = current_peer.peerallowedip_set.filter(config_file='client').order_by('priority') if current_peer.name: page_title += current_peer.name else: @@ -133,31 +135,28 @@ def view_wireguard_peer_manage(request): else: return redirect('/peer/list/') context = { - 'page_title': page_title, 'current_instance': current_instance, 'current_peer': current_peer, 'form': form, 'peer_ip_list': peer_ip_list + 'page_title': page_title, 'current_instance': current_instance, 'current_peer': current_peer, 'form': form, + 'peer_ip_list': peer_ip_list, 'peer_client_ip_list': peer_client_ip_list } return render(request, 'wireguard/wireguard_manage_peer.html', context) - def view_manage_ip_address(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_file = request.GET.get('config', 'server') + if request.GET.get('peer'): current_peer = get_object_or_404(Peer, uuid=request.GET.get('peer')) - page_title = 'Add new IP address for Peer ' + #page_title = 'Add new IP address for Peer ' + str(current_peer) current_ip = None - if current_peer.name: - page_title += current_peer.name - else: - page_title += current_peer.public_key elif request.GET.get('ip'): current_ip = get_object_or_404(PeerAllowedIP, uuid=request.GET.get('ip')) current_peer = current_ip.peer - page_title = 'Update IP address for Peer ' - if current_peer.name: - page_title += current_peer.name - else: - page_title += current_peer.public_key[:10] + ("..." if len(current_peer.public_key) > 16 else "") + config_file = current_ip.config_file + #page_title = 'Update IP address for Peer ' + str(current_peer) + if request.GET.get('action') == 'delete': if request.GET.get('confirmation') == 'delete': current_ip.delete() @@ -168,13 +167,20 @@ def view_manage_ip_address(request): else: messages.warning(request, 'Error deleting IP address|Invalid confirmation message. Type "delete" to confirm.') return redirect('/peer/ip/?ip=' + str(current_ip.uuid)) - + if config_file not in ['client', 'server']: + config_file = 'server' + if config_file == 'client': + page_title = 'Manage client route' + else: + page_title = 'Manage IP address or Network' + if request.method == 'POST': - form = PeerAllowedIPForm(request.POST or None, instance=current_ip, current_peer=current_peer) + form = PeerAllowedIPForm(request.POST or None, instance=current_ip, current_peer=current_peer, config_file=config_file) if form.is_valid(): this_form = form.save(commit=False) if not current_ip: this_form.peer = current_peer + this_form.config_file = config_file this_form.save() current_peer.wireguard_instance.pending_changes = True current_peer.wireguard_instance.save() diff --git a/wireguard_tools/views.py b/wireguard_tools/views.py index c69ffa2..ae85a3c 100644 --- a/wireguard_tools/views.py +++ b/wireguard_tools/views.py @@ -23,15 +23,18 @@ def generate_peer_config(peer_uuid): peer = get_object_or_404(Peer, uuid=peer_uuid) wg_instance = peer.wireguard_instance - priority_zero_ip = PeerAllowedIP.objects.filter(peer=peer, priority=0).first() + priority_zero_ip = PeerAllowedIP.objects.filter(config_file='server', peer=peer, priority=0).first() if not priority_zero_ip: return "No IP with priority zero found for this peer." client_address = f"{priority_zero_ip.allowed_ip}/{priority_zero_ip.netmask}" - #allowed_ips = PeerAllowedIP.objects.filter(peer=peer).exclude(uuid=priority_zero_ip.uuid).order_by('priority') - #allowed_ips_line = ", ".join([f"{ip.allowed_ip}/{ip.netmask}" for ip in allowed_ips]) + allowed_ips = PeerAllowedIP.objects.filter(peer=peer, config_file='client').order_by('priority') + if allowed_ips: + allowed_ips_line = ", ".join([f"{ip.allowed_ip}/{ip.netmask}" for ip in allowed_ips]) + else: + allowed_ips_line = "0.0.0.0/0, ::/0" config_lines = [ "[Interface]", @@ -41,7 +44,7 @@ def generate_peer_config(peer_uuid): "\n[Peer]", f"PublicKey = {wg_instance.public_key}", f"Endpoint = {wg_instance.hostname}:{wg_instance.listen_port}", - f"AllowedIPs = 0.0.0.0/0, ::/0", + f"AllowedIPs = {allowed_ips_line}", f"PresharedKey = {peer.pre_shared_key}" if peer.pre_shared_key else "", f"PersistentKeepalive = {peer.persistent_keepalive}", ] @@ -85,7 +88,7 @@ def export_wireguard_configs(request): rule_text_down = "" rule_destination = redirect_rule.ip_address if redirect_rule.peer: - peer_allowed_ip_address = PeerAllowedIP.objects.filter(peer=redirect_rule.peer, netmask=32, priority=0).first() + peer_allowed_ip_address = PeerAllowedIP.objects.filter(config_file='server', peer=redirect_rule.peer, netmask=32, priority=0).first() if peer_allowed_ip_address: rule_destination = peer_allowed_ip_address.allowed_ip if rule_destination: @@ -128,7 +131,7 @@ def export_wireguard_configs(request): 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 = PeerAllowedIP.objects.filter(config_file='server', 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) diff --git a/wireguard_webadmin/settings.py b/wireguard_webadmin/settings.py index 354941c..3cfc67b 100644 --- a/wireguard_webadmin/settings.py +++ b/wireguard_webadmin/settings.py @@ -129,6 +129,6 @@ STATICFILES_DIRS = [ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' -WIREGUARD_WEBADMIN_VERSION = 9507 +WIREGUARD_WEBADMIN_VERSION = 9601 from wireguard_webadmin.production_settings import * \ No newline at end of file