diff --git a/firewall/forms.py b/firewall/forms.py index 7f3a5d6..39d4816 100644 --- a/firewall/forms.py +++ b/firewall/forms.py @@ -53,44 +53,68 @@ class RedirectRuleForm(forms.ModelForm): class FirewallRuleForm(forms.ModelForm): - firewall_settings, firewall_settings_created = FirewallSettings.objects.get_or_create(name='global') - interface_list = [('', '------'),] - interface_list.append((firewall_settings.wan_interface, firewall_settings.wan_interface + ' (WAN)')) - - for wireguard_instance in WireGuardInstance.objects.all().order_by('instance_id'): - wireguard_instance_interface = 'wg'+ str(wireguard_instance.instance_id) - interface_list.append((wireguard_instance_interface, wireguard_instance_interface)) - - interface_list.append(('wg+', 'wg+ (Any WireGuard Interface)')) + def __init__(self, *args, **kwargs): + current_chain = kwargs.pop('current_chain', None) + super(FirewallRuleForm, self).__init__(*args, **kwargs) - description = forms.CharField(label='Description', required=False) - firewall_chain = forms.ChoiceField(label='Firewall Chain', choices=[('forward', 'FORWARD'), ('postrouting', 'POSTROUTING (nat)')], initial='forward') - in_interface = forms.ChoiceField(label='In Interface', choices=interface_list, required=False) - out_interface = forms.ChoiceField(label='Out Interface', choices=interface_list, required=False) - source_ip = forms.GenericIPAddressField(label='Source IP', required=False) - source_netmask = forms.IntegerField(label='Source Netmask', initial=32, min_value=0, max_value=32) - source_peer = forms.ModelMultipleChoiceField(label='Source Peer', queryset=Peer.objects.all(), required=False) - source_peer_include_networks = forms.BooleanField(label='Source Peer Include Networks', required=False) - not_source = forms.BooleanField(label='Not Source', required=False) - destination_ip = forms.GenericIPAddressField(label='Destination IP', required=False) - destination_netmask = forms.IntegerField(label='Destination Netmask', initial=32, min_value=0, max_value=32) - destination_peer = forms.ModelMultipleChoiceField(label='Destination Peer', queryset=Peer.objects.all(), required=False) - destination_peer_include_networks = forms.BooleanField(label='Destination Peer Include Networks', required=False) - not_destination = forms.BooleanField(label='Not Destination', required=False) - protocol = forms.ChoiceField(label='Protocol', choices=[('', 'all'), ('tcp', 'TCP'), ('udp', 'UDP'), ('both', 'TCP+UDP'), ('icmp', 'ICMP')], required=False) - destination_port = forms.CharField(label='Destination Port', required=False) - state_new = forms.BooleanField(label='State NEW', required=False) - state_related = forms.BooleanField(label='State RELATED', required=False) - state_established = forms.BooleanField(label='State ESTABLISHED', required=False) - state_invalid = forms.BooleanField(label='State INVALID', required=False) - state_untracked = forms.BooleanField(label='State UNTRACKED', required=False) - not_state = forms.BooleanField(label='Not State', required=False) - rule_action = forms.ChoiceField(label='Rule Action', initial='accept', choices=[('accept', 'ACCEPT'), ('reject', 'REJECT'), ('drop', 'DROP'), ('masquerade', 'MASQUERADE')]) - sort_order = forms.IntegerField(label='Sort Order', initial=0, min_value=0) + firewall_settings, firewall_settings_created = FirewallSettings.objects.get_or_create(name='global') + interface_list = [('', '------'),] + interface_list.append((firewall_settings.wan_interface, firewall_settings.wan_interface + ' (WAN)')) + + for wireguard_instance in WireGuardInstance.objects.all().order_by('instance_id'): + wireguard_instance_interface = 'wg'+ str(wireguard_instance.instance_id) + interface_list.append((wireguard_instance_interface, wireguard_instance_interface)) + + interface_list.append(('wg+', 'wg+ (Any WireGuard Interface)')) + + description = forms.CharField(label='Description', required=False) + firewall_chain = forms.ChoiceField(label='Firewall Chain', choices=[('forward', 'FORWARD'), ('postrouting', 'POSTROUTING (nat)')]) + in_interface = forms.ChoiceField(label='In Interface', required=False) + out_interface = forms.ChoiceField(label='Out Interface', required=False) + source_ip = forms.GenericIPAddressField(label='Source IP', required=False) + source_netmask = forms.IntegerField(label='Source Netmask', initial=32, min_value=0, max_value=32) + source_peer = forms.ModelMultipleChoiceField(label='Source Peer', queryset=Peer.objects.all(), required=False) + source_peer_include_networks = forms.BooleanField(label='Source Peer Include Networks', required=False) + not_source = forms.BooleanField(label='Not Source', required=False) + destination_ip = forms.GenericIPAddressField(label='Destination IP', required=False) + destination_netmask = forms.IntegerField(label='Destination Netmask', initial=32, min_value=0, max_value=32) + destination_peer = forms.ModelMultipleChoiceField(label='Destination Peer', queryset=Peer.objects.all(), required=False) + destination_peer_include_networks = forms.BooleanField(label='Destination Peer Include Networks', required=False) + not_destination = forms.BooleanField(label='Not Destination', required=False) + protocol = forms.ChoiceField(label='Protocol', choices=[('', 'all'), ('tcp', 'TCP'), ('udp', 'UDP'), ('both', 'TCP+UDP'), ('icmp', 'ICMP')], required=False) + destination_port = forms.CharField(label='Destination Port', required=False) + state_new = forms.BooleanField(label='State NEW', required=False) + state_related = forms.BooleanField(label='State RELATED', required=False) + state_established = forms.BooleanField(label='State ESTABLISHED', required=False) + state_invalid = forms.BooleanField(label='State INVALID', required=False) + state_untracked = forms.BooleanField(label='State UNTRACKED', required=False) + not_state = forms.BooleanField(label='Not State', required=False) + rule_action = forms.ChoiceField(label='Rule Action', initial='accept', choices=[('accept', 'ACCEPT'), ('reject', 'REJECT'), ('drop', 'DROP'), ('masquerade', 'MASQUERADE')]) + sort_order = forms.IntegerField(label='Sort Order', initial=0, min_value=0) + self.fields['firewall_chain'].initial = current_chain + self.fields['in_interface'].choices = interface_list + self.fields['out_interface'].choices = interface_list class Meta: model = FirewallRule fields = ['description', 'firewall_chain', 'in_interface', 'out_interface', 'source_ip', 'source_netmask', 'source_peer', 'source_peer_include_networks', 'not_source', 'destination_ip', 'destination_netmask', 'destination_peer', 'destination_peer_include_networks', 'not_destination', 'protocol', 'destination_port', 'state_new', 'state_related', 'state_established', 'state_invalid', 'state_untracked', 'not_state', 'rule_action', 'sort_order'] + def clean(self): + cleaned_data = super().clean() + firewall_chain = cleaned_data.get('firewall_chain') + rule_action = cleaned_data.get('rule_action') + in_interface = cleaned_data.get('in_interface') + + if firewall_chain == 'forward' and rule_action not in ['accept', 'drop', 'reject']: + raise forms.ValidationError("Invalid rule action for firewall chain 'forward'. Allowed actions are 'accept', 'drop', and 'reject'.") + + if firewall_chain == 'postrouting': + if rule_action not in ['masquerade', 'accept']: + raise forms.ValidationError("Invalid rule action for firewall chain 'postrouting'. Allowed actions are 'masquerade' and 'accept'.") + if in_interface: + raise forms.ValidationError("In Interface cannot be used with firewall chain 'postrouting'.") + + return cleaned_data + diff --git a/firewall/views.py b/firewall/views.py index 62775cf..56f752c 100644 --- a/firewall/views.py +++ b/firewall/views.py @@ -1,4 +1,5 @@ from django.shortcuts import render, get_object_or_404, redirect +from django.db.models import Max from firewall.models import RedirectRule, FirewallRule, FirewallSettings from firewall.forms import RedirectRuleForm, FirewallRuleForm from django.contrib import messages @@ -82,6 +83,7 @@ def manage_firewall_rule(request): uuid = request.GET.get('uuid', None) if uuid: instance = get_object_or_404(FirewallRule, uuid=uuid) + current_chain = instance.firewall_chain if request.GET.get('action') == 'delete': if request.GET.get('confirmation') == 'delete': firewall_settings, firewall_settings_created = FirewallSettings.objects.get_or_create(name='global') @@ -91,19 +93,32 @@ def manage_firewall_rule(request): else: messages.warning(request, 'Error deleting Firewall rule|Confirmation did not match. Firewall rule was not deleted.') return redirect('/firewall/rule_list/') + else: + current_chain = request.GET.get('chain', 'forward') if request.method == 'POST': - form = FirewallRuleForm(request.POST, instance=instance) + form = FirewallRuleForm(request.POST, instance=instance, current_chain=current_chain) if form.is_valid(): firewall_settings, firewall_settings_created = FirewallSettings.objects.get_or_create(name='global') firewall_settings.pending_changes = True firewall_settings.save() form.save() messages.success(request, 'Firewall rule saved successfully') - return redirect('/firewall/rule_list/') + return redirect('/firewall/rule_list/?chain=' + current_chain) else: - form = FirewallRuleForm(instance=instance) + form = FirewallRuleForm(instance=instance, current_chain=current_chain) context['form'] = form context['instance'] = instance + + highest_forward_sort_order = FirewallRule.objects.filter(firewall_chain='forward').aggregate(Max('sort_order'))['sort_order__max'] + if highest_forward_sort_order is None: + highest_forward_sort_order = 0 + + highest_postrouting_sort_order = FirewallRule.objects.filter(firewall_chain='postrouting').aggregate(Max('sort_order'))['sort_order__max'] + if highest_postrouting_sort_order is None: + highest_postrouting_sort_order = 0 + + context['forward_sort_order'] = highest_forward_sort_order + 10 + context['postrouting_sort_order'] = highest_postrouting_sort_order + 10 return render(request, 'firewall/manage_firewall_rule.html', context=context) diff --git a/templates/firewall/firewall_rule_list.html b/templates/firewall/firewall_rule_list.html index 031e2d7..6d61c2e 100644 --- a/templates/firewall/firewall_rule_list.html +++ b/templates/firewall/firewall_rule_list.html @@ -87,7 +87,7 @@ - Create Firewall Rule + Create Firewall Rule @@ -102,42 +102,6 @@ {% endblock %} {% block custom_page_scripts %} -{% comment %} - -{% endcomment %} {% endblock %} \ No newline at end of file diff --git a/templates/firewall/manage_firewall_rule.html b/templates/firewall/manage_firewall_rule.html index aecb501..688ffef 100644 --- a/templates/firewall/manage_firewall_rule.html +++ b/templates/firewall/manage_firewall_rule.html @@ -431,4 +431,29 @@ + + + + {% endblock %}