diff --git a/plinth/modules/wireguard/forms.py b/plinth/modules/wireguard/forms.py
index 87450ac32..cccc37ad8 100644
--- a/plinth/modules/wireguard/forms.py
+++ b/plinth/modules/wireguard/forms.py
@@ -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
diff --git a/plinth/modules/wireguard/templates/wireguard_show_server.html b/plinth/modules/wireguard/templates/wireguard_show_server.html
index 4f886ccfb..f0f306c95 100644
--- a/plinth/modules/wireguard/templates/wireguard_show_server.html
+++ b/plinth/modules/wireguard/templates/wireguard_show_server.html
@@ -36,9 +36,9 @@
{% trans "Public key of this machine:" %} |
{{ server.public_key }} |
-
+
| {% trans "IP address of this machine:" %} |
- {{ server.ip_address }} |
+ {{ server.ip_address_and_network }} |
|
diff --git a/plinth/modules/wireguard/tests/test_forms.py b/plinth/modules/wireguard/tests/test_forms.py
index 3e4c33d9b..780cb8d4a 100644
--- a/plinth/modules/wireguard/tests/test_forms.py
+++ b/plinth/modules/wireguard/tests/test_forms.py
@@ -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)
diff --git a/plinth/modules/wireguard/tests/test_functional.py b/plinth/modules/wireguard/tests/test_functional.py
index 02f60a36d..bc2714440 100644
--- a/plinth/modules/wireguard/tests/test_functional.py
+++ b/plinth/modules/wireguard/tests/test_functional.py
@@ -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')
diff --git a/plinth/modules/wireguard/utils.py b/plinth/modules/wireguard/utils.py
index c7915bac3..5917ade72 100644
--- a/plinth/modules/wireguard/utils.py
+++ b/plinth/modules/wireguard/utils.py
@@ -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
diff --git a/plinth/modules/wireguard/views.py b/plinth/modules/wireguard/views.py
index 545acfbc1..e561bd245 100644
--- a/plinth/modules/wireguard/views.py
+++ b/plinth/modules/wireguard/views.py
@@ -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']
diff --git a/plinth/network.py b/plinth/network.py
index 873bcd5bb..657d73363 100644
--- a/plinth/network.py
+++ b/plinth/network.py
@@ -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)
|