mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-03-11 09:04:54 +00:00
wireguard: Accept/use netmask with IP address for server connection
- Currently, the value is hard-coded as /24. Instead take this as input and use that value. Tests: - Entering invalid IPv4 address results in 'Enter a valid IPv4 address' error message during form submission. - Entering invalid prefix such as /33 results in 'Enter a valid network prefix or net mask.' error during form submission. - Both /32 and /255.255.255.255 formats are accepted. - The description text for the form field 'IP address' is as expected. - Changing the value of default route and IP address + netmask reflects in the status page. Correct values is shown in the edit server and server status page. - Not providing a netmask results in /32 being assigned. - Unit and functional tests for wireguard pass. There are some intermittent failures with functional tests that are unrelated to the patch. - Setting the /32 prefix results in correct routing table as shown by 'ip route show table all'. No default routes are network routes are present. 'traceroute 1.1.1.1' shows route taken via regular network. - Setting the /24 prefix results in correct routing table. No default routes are present. However, for the /24 network a route is present with device wg1. 'traceroute 1.1.1.1' shows route taken via regular network. - Enabling the default route results in correct routing table. Default route is shown for device wg1 with high priority. 'traceroute 1.1.1.1' shows route taken via WireGuard network. Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
parent
ad9ebe2301
commit
4b24fda3f5
@ -5,10 +5,10 @@ Forms for wireguard module.
|
||||
|
||||
import base64
|
||||
import binascii
|
||||
import ipaddress
|
||||
|
||||
from django import forms
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.validators import validate_ipv4_address
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
KEY_LENGTH = 32
|
||||
@ -55,6 +55,16 @@ def validate_endpoint(endpoint):
|
||||
raise ValidationError('Invalid endpoint.')
|
||||
|
||||
|
||||
def validate_ipv4_address_with_network(value: str):
|
||||
"""Check that value is a valid IPv4 address with an optional network."""
|
||||
try:
|
||||
ipaddress.IPv4Interface(value)
|
||||
except ipaddress.AddressValueError:
|
||||
raise ValidationError(_('Enter a valid IPv4 address.'))
|
||||
except ipaddress.NetmaskValueError:
|
||||
raise ValidationError(_('Enter a valid network prefix or net mask.'))
|
||||
|
||||
|
||||
class AddClientForm(forms.Form):
|
||||
"""Form to add client."""
|
||||
public_key = forms.CharField(
|
||||
@ -78,12 +88,15 @@ class AddServerForm(forms.Form):
|
||||
'Example: MConEJFIg6+DFHg2J1nn9SNLOSE9KR0ysdPgmPjibEs= .'),
|
||||
validators=[validate_key])
|
||||
|
||||
ip_address = forms.CharField(
|
||||
ip_address_and_network = forms.CharField(
|
||||
label=_('Client IP address provided by server'), strip=True,
|
||||
help_text=_('IP address assigned to this machine on the VPN after '
|
||||
'connecting to the endpoint. This value is usually '
|
||||
'provided by the server operator. Example: 192.168.0.10.'),
|
||||
validators=[validate_ipv4_address])
|
||||
help_text=_(
|
||||
'IP address assigned to this machine on the VPN after connecting '
|
||||
'to the endpoint. This value is usually provided by the server '
|
||||
'operator. Example: 192.168.0.10. You can also specify the '
|
||||
'network. This will allow reaching machines in the network. '
|
||||
'Examples: 10.68.12.43/24 or 10.68.12.43/255.255.255.0.'),
|
||||
validators=[validate_ipv4_address_with_network])
|
||||
|
||||
private_key = forms.CharField(
|
||||
label=_('Private key of this machine'), strip=True, help_text=_(
|
||||
@ -107,9 +120,18 @@ class AddServerForm(forms.Form):
|
||||
'Typically checked for a VPN service through which all traffic '
|
||||
'is sent.'))
|
||||
|
||||
def get_settings(self):
|
||||
def get_settings(self) -> dict[str, dict]:
|
||||
"""Return NM settings dict from cleaned data."""
|
||||
ip_address = self.cleaned_data['ip_address']
|
||||
ip_address_and_network = self.cleaned_data['ip_address_and_network']
|
||||
ip_address_and_network = ipaddress.IPv4Interface(
|
||||
ip_address_and_network)
|
||||
ip_address = str(ip_address_and_network.ip)
|
||||
prefixlen = ip_address_and_network.network.prefixlen
|
||||
if self.cleaned_data['default_route']:
|
||||
allowed_ips = ['0.0.0.0/0', '::/0']
|
||||
else:
|
||||
allowed_ips = [f'{ip_address}/{prefixlen}']
|
||||
|
||||
settings = {
|
||||
'common': {
|
||||
'type': 'wireguard',
|
||||
@ -118,7 +140,7 @@ class AddServerForm(forms.Form):
|
||||
'ipv4': {
|
||||
'method': 'manual',
|
||||
'address': ip_address,
|
||||
'netmask': '255.255.255.0',
|
||||
'netmask': str(ip_address_and_network.netmask),
|
||||
'gateway': '',
|
||||
'dns': '',
|
||||
'second_dns': '',
|
||||
@ -126,10 +148,9 @@ class AddServerForm(forms.Form):
|
||||
'wireguard': {
|
||||
'peer_endpoint': self.cleaned_data['peer_endpoint'],
|
||||
'peer_public_key': self.cleaned_data['peer_public_key'],
|
||||
'ip_address': ip_address,
|
||||
'private_key': self.cleaned_data['private_key'],
|
||||
'preshared_key': self.cleaned_data['preshared_key'],
|
||||
'default_route': self.cleaned_data['default_route'],
|
||||
'allowed_ips': allowed_ips,
|
||||
}
|
||||
}
|
||||
return settings
|
||||
|
||||
@ -36,9 +36,9 @@
|
||||
<th>{% trans "Public key of this machine:" %}</th>
|
||||
<td>{{ server.public_key }}</td>
|
||||
</tr>
|
||||
<tr class="server-ip-address">
|
||||
<tr class="server-ip-address-and-network">
|
||||
<th>{% trans "IP address of this machine:" %}</th>
|
||||
<td>{{ server.ip_address }}</td>
|
||||
<td>{{ server.ip_address_and_network }}</td>
|
||||
</tr>
|
||||
<tr class="server-default-route">
|
||||
<th>
|
||||
|
||||
@ -6,7 +6,9 @@ Tests for wireguard module forms.
|
||||
import pytest
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
from plinth.modules.wireguard.forms import validate_endpoint, validate_key
|
||||
from plinth.modules.wireguard.forms import (validate_endpoint,
|
||||
validate_ipv4_address_with_network,
|
||||
validate_key)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('key', [
|
||||
@ -62,3 +64,31 @@ def test_validate_endpoint_invalid_patterns(endpoint):
|
||||
"""Test that invalid wireguard endpoint patterns are rejected."""
|
||||
with pytest.raises(ValidationError):
|
||||
validate_endpoint(endpoint)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('value', [
|
||||
'1.2.3.4',
|
||||
'1.2.3.4/0',
|
||||
'1.2.3.4/32',
|
||||
'1.2.3.4/24',
|
||||
'1.2.3.4/255.255.255.0',
|
||||
'1.2.3.4/0.0.0.255',
|
||||
])
|
||||
def test_validate_ipv4_address_with_network_valid_patterns(value):
|
||||
"""Test validating IPv4 address with network works for valid values."""
|
||||
validate_ipv4_address_with_network(value)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('value', [
|
||||
'::1',
|
||||
'1.2.3.4/',
|
||||
'invalid-ip/24',
|
||||
'1.2.3.4/x',
|
||||
'1.2.3.4/-1',
|
||||
'1.2.3.4/33',
|
||||
'1.2.3.4/9.8.7.6',
|
||||
])
|
||||
def test_validate_ipv4_address_with_network_invalid_patterns(value):
|
||||
"""Test validating IPv4 address with network works for invalid values."""
|
||||
with pytest.raises(ValidationError):
|
||||
validate_ipv4_address_with_network(value)
|
||||
|
||||
@ -23,14 +23,14 @@ class TestWireguardApp(functional.BaseAppTests):
|
||||
{
|
||||
'peer_endpoint': 'wg1.example.org:1234',
|
||||
'peer_public_key': 'HBCqZk4B93N6q19zNleJkAVs+PEfWAPgPpKnrhL/CVw=',
|
||||
'ip_address': '10.0.0.2',
|
||||
'ip_address_and_network': '10.0.0.2/32',
|
||||
'private_key': '',
|
||||
'preshared_key': ''
|
||||
},
|
||||
{
|
||||
'peer_endpoint': 'wg2.example.org:5678',
|
||||
'peer_public_key': 'Z/iHo0vaeSN8Ykk5KwhQ819MMU5nyzD7y7xFFthlxXI=',
|
||||
'ip_address': '192.168.0.2',
|
||||
'ip_address_and_network': '192.168.0.2/24',
|
||||
'private_key': 'QC2xEZMn3bgNsSVFrU51+ALSUiUaWg6gRWigh3EeVm0=',
|
||||
'preshared_key': 'AHxZ4Rr8Ij4L1aq+ceusSIgBfluqiI9Vb5I2UtQFanI='
|
||||
},
|
||||
@ -70,7 +70,8 @@ class TestWireguardApp(functional.BaseAppTests):
|
||||
# Start the server on FreedomBox, if needed.
|
||||
start_server_button = browser.find_by_css('.btn-start-server')
|
||||
if start_server_button:
|
||||
start_server_button.first.click()
|
||||
with functional.wait_for_page_update(browser):
|
||||
start_server_button.first.click()
|
||||
|
||||
browser.find_by_css('.btn-add-client').first.click()
|
||||
browser.find_by_id('id_public_key').fill(key)
|
||||
@ -135,7 +136,8 @@ class TestWireguardApp(functional.BaseAppTests):
|
||||
href.first.click()
|
||||
assert get_value('peer-endpoint') == config['peer_endpoint']
|
||||
assert get_value('peer-public-key') == config['peer_public_key']
|
||||
assert get_value('server-ip-address') == config['ip_address']
|
||||
assert get_value('server-ip-address-and-network'
|
||||
) == config['ip_address_and_network']
|
||||
assert get_value('peer-preshared-key') == (config['preshared_key']
|
||||
or 'None')
|
||||
|
||||
@ -147,7 +149,8 @@ class TestWireguardApp(functional.BaseAppTests):
|
||||
browser.find_by_id('id_peer_endpoint').fill(config['peer_endpoint'])
|
||||
browser.find_by_id('id_peer_public_key').fill(
|
||||
config['peer_public_key'])
|
||||
browser.find_by_id('id_ip_address').fill(config['ip_address'])
|
||||
browser.find_by_id('id_ip_address_and_network').fill(
|
||||
config['ip_address_and_network'])
|
||||
browser.find_by_id('id_private_key').fill(config['private_key'])
|
||||
browser.find_by_id('id_preshared_key').fill(config['preshared_key'])
|
||||
functional.submit(browser, form_class='form-add-server')
|
||||
@ -161,7 +164,8 @@ class TestWireguardApp(functional.BaseAppTests):
|
||||
browser.find_by_id('id_peer_endpoint').fill(config2['peer_endpoint'])
|
||||
browser.find_by_id('id_peer_public_key').fill(
|
||||
config2['peer_public_key'])
|
||||
browser.find_by_id('id_ip_address').fill(config2['ip_address'])
|
||||
browser.find_by_id('id_ip_address_and_network').fill(
|
||||
config2['ip_address_and_network'])
|
||||
browser.find_by_id('id_private_key').fill(config2['private_key'])
|
||||
browser.find_by_id('id_preshared_key').fill(config2['preshared_key'])
|
||||
functional.submit(browser, form_class='form-edit-server')
|
||||
|
||||
@ -65,7 +65,9 @@ def get_nm_info():
|
||||
|
||||
settings_ipv4 = connection.get_setting_ip4_config()
|
||||
if settings_ipv4 and settings_ipv4.get_num_addresses():
|
||||
info['ip_address'] = settings_ipv4.get_address(0).get_address()
|
||||
address = settings_ipv4.get_address(0)
|
||||
info['ip_address_and_network'] = (address.get_address() + '/' +
|
||||
str(address.get_prefix()))
|
||||
|
||||
connections[info['interface']] = info
|
||||
|
||||
|
||||
@ -101,8 +101,7 @@ class ShowClientView(SuccessMessageMixin, TemplateView):
|
||||
context['client'] = server_info['peers'][public_key]
|
||||
context['endpoints'] = [
|
||||
domain + ':' + str(server_info['listen_port'])
|
||||
for domain in domains
|
||||
if not domain.endswith('.local')
|
||||
for domain in domains if not domain.endswith('.local')
|
||||
]
|
||||
return context
|
||||
|
||||
@ -227,7 +226,8 @@ class EditServerView(SuccessMessageMixin, FormView):
|
||||
if not server:
|
||||
raise Http404
|
||||
|
||||
initial['ip_address'] = server.get('ip_address')
|
||||
initial['ip_address_and_network'] = server.get(
|
||||
'ip_address_and_network')
|
||||
if server['peers']:
|
||||
peer = next(peer for peer in server['peers'].values())
|
||||
initial['peer_endpoint'] = peer['endpoint']
|
||||
|
||||
@ -507,12 +507,8 @@ def _update_wireguard_settings(connection, wireguard):
|
||||
peer.set_preshared_key_flags(nm.SettingSecretFlags.NONE)
|
||||
peer.set_preshared_key(wireguard['preshared_key'], False)
|
||||
|
||||
if wireguard['default_route']:
|
||||
peer.append_allowed_ip('0.0.0.0/0', False)
|
||||
peer.append_allowed_ip('::/0', False)
|
||||
else:
|
||||
ip_addr = wireguard['ip_address']
|
||||
peer.append_allowed_ip(f'{ip_addr}/24', False)
|
||||
for allowed_ip in wireguard['allowed_ips']:
|
||||
peer.append_allowed_ip(allowed_ip, False)
|
||||
|
||||
settings.clear_peers()
|
||||
settings.append_peer(peer)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user