Improved clean at firewall rule form

Better integration with port forward rules.
This commit is contained in:
Eduardo Silva 2024-03-01 10:41:00 -03:00
parent 015d9b0927
commit 5f8627e3f3
8 changed files with 63 additions and 27 deletions

View File

@ -1,6 +1,7 @@
from firewall.models import RedirectRule, FirewallRule, FirewallSettings
from wireguard.models import Peer, WireGuardInstance, NETMASK_CHOICES
from django import forms
import re
class RedirectRuleForm(forms.ModelForm):
@ -104,6 +105,8 @@ class FirewallRuleForm(forms.ModelForm):
firewall_chain = cleaned_data.get('firewall_chain')
rule_action = cleaned_data.get('rule_action')
in_interface = cleaned_data.get('in_interface')
destination_port = self.cleaned_data.get('destination_port')
protocol = cleaned_data.get('protocol')
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'.")
@ -113,6 +116,21 @@ class FirewallRuleForm(forms.ModelForm):
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'.")
if destination_port:
if protocol not in ['tcp', 'udp', 'both']:
raise forms.ValidationError("Destination Port can only be used with protocol 'tcp' and/or 'udp'.")
if not re.match(r'^(\d+|\d+:\d+)$', destination_port):
raise forms.ValidationError("Invalid destination port format. Use a single port number or a range of port numbers separated by a colon. Example: 80 or 8000:8080.")
if ':' in destination_port:
start, end = map(int, destination_port.split(':'))
if not 1 <= start <= 65535 or not 1 <= end <= 65535 or start >= end:
raise forms.ValidationError("Invalid port range. The start and end port numbers must be between 1 and 65535 and the start port number must be less than the end port number.")
else:
port = int(destination_port)
if not 1 <= port <= 65535:
raise forms.ValidationError("Invalid port number. The port number must be between 1 and 65535.")
return cleaned_data

View File

@ -89,6 +89,7 @@ def manage_firewall_rule(request):
firewall_settings, firewall_settings_created = FirewallSettings.objects.get_or_create(name='global')
firewall_settings.pending_changes = True
firewall_settings.save()
instance.delete()
messages.success(request, 'Firewall rule deleted successfully')
else:
messages.warning(request, 'Error deleting Firewall rule|Confirmation did not match. Firewall rule was not deleted.')
@ -120,5 +121,6 @@ def manage_firewall_rule(request):
context['forward_sort_order'] = highest_forward_sort_order + 10
context['postrouting_sort_order'] = highest_postrouting_sort_order + 10
context['current_chain'] = current_chain
return render(request, 'firewall/manage_firewall_rule.html', context=context)

View File

@ -104,10 +104,11 @@
</li>
<li class="nav-item">
<a href="/firewall/port_forward/" class="nav-link {% if '/firewall/' in request.path %}active{% endif %}">
<i class="fas fa-directions nav-icon"></i>
<a href="/firewall/rule_list/" class="nav-link {% if '/firewall/' in request.path %}active{% endif %}">
<i class="fas fa-shield-alt nav-icon"></i>
<p>
Port Forwarding
Firewall
</p>
</a>
</li>

View File

@ -2,11 +2,12 @@
<li class="nav-item">
<a class="nav-link {% if current_chain == "forward" %}active{% endif %}" href="/firewall/rule_list/?chain=forward" role="tab">Forward</a>
</li>
<li class="nav-item">
<a class="nav-link {% if current_chain == "portforward" %}active{% endif %}" href="/firewall/port_forward/" role="tab">Port Forward</a>
</li>
<li class="nav-item">
<a class="nav-link {% if current_chain == "postrouting" %}active{% endif %}" href="/firewall/rule_list/?chain=postrouting" role="tab">Post Routing</a>
</li>
<li class="nav-item">
<a class="nav-link {% if current_chain == "portforward" %}active{% endif %}" href="/firewall/port_forward/" role="tab">Port Forward</a>
</li>
</ul>

View File

@ -44,8 +44,6 @@
<th>State</th>
<th>Action</th>
<th></th>
</thead>
<tbody>
{% for rule in firewall_rule_list %}
@ -53,8 +51,8 @@
<tr>
<td style="width: 1%; white-space: nowrap;">{{ rule.sort_order }}</td>
<td style="width: 1%; white-space: nowrap;">{% if rule.description %}<i class="fas fa-info-circle" title="{{ rule.description }}"></i>{% endif %}</td>
<td>{{ rule.in_interface }}</td>
<td>{{ rule.out_interface }}</td>
<td>{{ rule.in_interface|default_if_none:'' }}</td>
<td>{{ rule.out_interface|default_if_none:'' }}</td>
<td>
{% if rule.source_ip %}{% if rule.not_source %}<span title="Not source">!</span> {% endif %}{{ rule.source_ip }}/{{ rule.source_netmask }}<br>{% endif%}
{% for peer in rule.source_peer.all %}{% if rule.not_source %}<span title="Not source">!</span> {% endif %}{{ peer }}{% if rule.source_peer_include_networks %} <span title="Include peer networks">+</span>{% endif %}<br>{% endfor %}
@ -65,8 +63,8 @@
{% for peer in rule.destination_peer.all %}{% if rule.not_destination %}<span title="Not destination">!</span> {% endif %}{{ peer }}{% if rule.destination_peer_include_networks %} <span title="Include peer networks">+</span>{% endif %}<br>{% endfor %}
</td>
<td>{{ rule.get_protocol_display }}</td>
<td>{{ rule.destination_port }}</td>
<td>{{ rule.get_protocol_display|default_if_none:'' }}</td>
<td>{{ rule.destination_port|default_if_none:'' }}</td>
<td>
{% if rule.state_new %}{% if rule.not_state %}<span title="Not state">! </span>{% endif %}New<br>{% endif %}
{% if rule.state_related %}{% if rule.not_state %}<span title="Not state">! </span>{% endif %}Related<br>{% endif %}

View File

@ -370,37 +370,42 @@
</div>
<div class="card-footer">
<button type="submit" class="btn btn-primary">Submit</button>
<a class="btn btn-outline-secondary" href="/firewall/rule_list/?chain={{ current_chain }}">Back</a>
{% if instance %}
<a href='javascript:void(0)' class='btn btn-outline-danger' data-command='delete' onclick='openCommandDialog(this)'>Delete Rule</a>
{% endif %}
</div>
</form>
</div>
</div>
{% endblock %}
{% block custom_page_scripts %}
<script>
document.addEventListener("DOMContentLoaded", function() {
// Array de IDs dos campos a serem ignorados
var ignoreFields = ['source_netmask', 'destination_netmask'];
// Itera por cada painel para verificar se contém dados nos campos
$('.collapse').each(function() {
var panel = $(this);
var shouldOpen = false;
// Verifica inputs do tipo texto e número, excluindo os ignorados
panel.find('input[type=text], input[type=number], textarea').each(function() {
if (!ignoreFields.includes(this.id) && $(this).val()) {
shouldOpen = true;
}
});
// Verifica checkboxes e radios, excluindo os ignorados
panel.find('input[type=checkbox], input[type=radio]').each(function() {
if (!ignoreFields.includes(this.id) && $(this).is(':checked')) {
shouldOpen = true;
}
});
// Verifica selects, incluindo múltipla seleção, excluindo os ignorados
panel.find('select').each(function() {
if (!ignoreFields.includes(this.id) && $(this).find('option:selected').length > 0) {
var allUnselected = true;
@ -413,15 +418,12 @@
shouldOpen = true;
}
}
});
// Se dados relevantes foram encontrados e não são para ser ignorados, abre o painel
});
if (shouldOpen) {
panel.collapse('show');
}
});
// Controla o abrir/fechar dos painéis sem afetar os outros
$('.card-header button').on('click', function(e) {
e.preventDefault();
var target = $(this).attr('data-target');
@ -429,7 +431,6 @@
});
});
</script>
@ -454,6 +455,20 @@
updateSortOrder();
{% endif %}
});
</script>
{% endblock %}
</script>
<script>
function openCommandDialog(element) {
var command = element.getAttribute('data-command');
var confirmation = prompt("Please type 'delete' to remove this firewall rule.");
if (confirmation) {
var url = "?uuid={{ instance.uuid }}&action=delete&confirmation=" + encodeURIComponent(confirmation);
window.location.href = url;
}
}
</script>
{% endblock %}

View File

@ -80,7 +80,7 @@
</div>
<button type="submit" class="btn btn-primary">Submit</button>
<a href="/firewall/port_forward/" class="btn btn-secondary">Cancel</a>
<a href="/firewall/port_forward/" class="btn btn-outline-secondary">Back</a>
{% if instance %}<a href='javascript:void(0)' class='btn btn-outline-danger' data-command='delete' onclick='openCommandDialog(this)'>Delete Rule</a>{% endif %}
</form>

View File

@ -66,6 +66,7 @@
</tbody>
</table>
<a href="/firewall/manage_port_forward_rule/" class='btn btn-primary'>Create Port forwarding Rule</a>
</div>
</div>
@ -75,5 +76,5 @@
<a href="/firewall/manage_port_forward_rule/" class='btn btn-primary'>Create Port forwarding Rule</a>
{% endblock %}