diff --git a/actions/wireguard b/actions/wireguard index 3856f795e..3558bb79f 100755 --- a/actions/wireguard +++ b/actions/wireguard @@ -21,99 +21,23 @@ Configuration helper for WireGuard. import argparse import json -import os -import pathlib import subprocess -PUBLIC_KEY_HELP = 'Public key for the client' - SERVER_INTERFACE = 'wg0' -KEY_FOLDER = pathlib.Path('/var/lib/freedombox/wireguard') -PRIVATE_KEY_PATH = KEY_FOLDER / 'privatekey' -PUBLIC_KEY_PATH = KEY_FOLDER / 'publickey' - - -class InterfaceNotFoundError(Exception): - """Exception raised when no matching interface is found.""" - def parse_arguments(): """Return parsed command line arguments as dictionary.""" parser = argparse.ArgumentParser() subparsers = parser.add_subparsers(dest='subcommand', help='Sub command') - subparsers.add_parser('setup', help='Setup WireGuard') subparsers.add_parser('get-info', help='Get info for each configured interface') - add_client = subparsers.add_parser('add-client', help='Add a client') - add_client.add_argument('publickey', help=PUBLIC_KEY_HELP) - - remove_client = subparsers.add_parser('remove-client', - help='Remove a client') - remove_client.add_argument('publickey', help=PUBLIC_KEY_HELP) - - modify_server = subparsers.add_parser('modify-server', - help='Modify a server') - modify_server.add_argument('--endpoint', required=True, - help='Server endpoint') - modify_server.add_argument('--client-ip', required=True, - help='Client IP address provided by server') - modify_server.add_argument('--public-key', required=True, - help='Public key of the server') - modify_server.add_argument('--pre-shared-key', help='Pre-shared key') - modify_server.add_argument( - '--all-outgoing', action='store_true', - help='Use this connection to send all outgoing traffic') - - remove_server = subparsers.add_parser('remove-server', - help='Remove a server') - remove_server.add_argument('publickey', help=PUBLIC_KEY_HELP) - subparsers.required = True return parser.parse_args() -def _generate_key_pair(): - """Generate private/public key pair.""" - private_key = subprocess.check_output(['wg', 'genkey']) - public_key = subprocess.check_output(['wg', 'pubkey'], input=private_key) - KEY_FOLDER.mkdir(parents=True, exist_ok=True) - with PUBLIC_KEY_PATH.open(mode='wb') as public_key_file: - public_key_file.write(public_key) - - old_umask = os.umask(0o077) - try: - with PRIVATE_KEY_PATH.open(mode='wb') as private_key_file: - private_key_file.write(private_key) - - finally: - os.umask(old_umask) - - -def subcommand_setup(_): - """Setup WireGuard.""" - # Create interface. - try: - subprocess.run(['ip', 'link', 'show', SERVER_INTERFACE], - stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, - check=True) - except subprocess.CalledProcessError: - subprocess.run([ - 'ip', 'link', 'add', 'dev', SERVER_INTERFACE, 'type', 'wireguard' - ], check=True) - - if not (PUBLIC_KEY_PATH.exists() and PRIVATE_KEY_PATH.exists()): - _generate_key_pair() - - # Configure interface. - subprocess.run([ - 'wg', 'set', SERVER_INTERFACE, 'listen-port', '51820', 'private-key', - str(PRIVATE_KEY_PATH) - ], check=True) - - def _get_info(): """Return info for each configured interface.""" output = subprocess.check_output(['wg', 'show', 'all', @@ -125,14 +49,16 @@ def _get_info(): continue fields = line.split() + fields = [field if field != '(none)' else None for field in fields] interface_name = fields[0] if interface_name in interfaces: + latest_handshake = int(fields[5]) if int(fields[5]) else None peer = { 'public_key': fields[1], 'preshared_key': fields[2], 'endpoint': fields[3], 'allowed_ips': fields[4], - 'latest_handshake': fields[5], + 'latest_handshake': latest_handshake, 'transfer_rx': fields[6], 'transfer_tx': fields[7], 'persistent_keepalive': fields[8], @@ -157,20 +83,6 @@ def subcommand_get_info(_): print(json.dumps(_get_info())) -def subcommand_add_client(arguments): - """Add a client.""" - subprocess.run( - ['wg', 'set', SERVER_INTERFACE, 'peer', arguments.publickey], - check=True) - - -def subcommand_remove_client(arguments): - """Remove a client.""" - subprocess.run( - ['wg', 'set', SERVER_INTERFACE, 'peer', arguments.publickey, 'remove'], - check=True) - - def main(): """Parse arguments and perform all duties.""" arguments = parse_arguments() diff --git a/plinth/modules/wireguard/__init__.py b/plinth/modules/wireguard/__init__.py index 7cdb630e0..26d4bb552 100644 --- a/plinth/modules/wireguard/__init__.py +++ b/plinth/modules/wireguard/__init__.py @@ -18,13 +18,9 @@ FreedomBox app for wireguard. """ -import datetime -import json - from django.urls import reverse_lazy from django.utils.translation import ugettext_lazy as _ -from plinth import actions from plinth import app as app_module from plinth import cfg, frontpage, menu from plinth.modules.firewall.components import Firewall @@ -102,98 +98,4 @@ def init(): def setup(helper, old_version=None): """Install and configure the module.""" helper.install(managed_packages) - helper.call('post', actions.superuser_run, 'wireguard', ['setup']) helper.call('post', app.enable) - - -def get_public_key(): - """Return this box's public key.""" - public_key_path = '/var/lib/freedombox/wireguard/publickey' - try: - with open(public_key_path) as public_key_file: - public_key = public_key_file.read().strip() - - except FileNotFoundError: - public_key = None - - return public_key - - -def get_nm_info(): - """Get information from network manager.""" - client = nm.Client.new(None) - - connections = {} - for connection in client.get_connections(): - if connection.get_connection_type() != 'wireguard': - continue - - settings = connection.get_setting_by_name('wireguard') - secrets = connection.get_secrets('wireguard') - connection.update_secrets('wireguard', secrets) - - info = {} - info['interface'] = connection.get_interface_name() - info['private_key'] = settings.get_private_key() - info['listen_port'] = settings.get_listen_port() - info['fwmark'] = settings.get_fwmark() - info['mtu'] = settings.get_mtu() - info['default_route'] = settings.get_ip4_auto_default_route() - info['peers'] = [] - for peer_index in range(settings.get_peers_len()): - peer = settings.get_peer(peer_index) - peer_info = { - 'endpoint': peer.get_endpoint(), - 'public_key': peer.get_public_key(), - 'preshared_key': peer.get_preshared_key(), - 'persistent_keepalive': peer.get_persistent_keepalive(), - 'allowed_ips': [] - } - for index in range(peer.get_allowed_ips_len()): - allowed_ip = peer.get_allowed_ip(index, None) - peer_info['allowed_ips'].append(allowed_ip) - - info['peers'].append(peer_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() - - connections[info['interface']] = info - - return connections - - -def get_info(): - """Return server and clients info.""" - output = actions.superuser_run('wireguard', ['get-info']) - status = json.loads(output) - - nm_info = get_nm_info() - - my_server_info = status.pop(SERVER_INTERFACE, {}) - my_client_servers = {} - - for interface, info in nm_info.items(): - my_client_servers[interface] = info - - if interface not in status: - continue - - for info_peer in info['peers']: - for status_peer in status[interface]['peers']: - if info_peer['public_key'] == status_peer['public_key']: - info_peer['status'] = status_peer - status_peer['latest_handshake'] = \ - datetime.datetime.fromtimestamp( - int(status_peer['latest_handshake'])) - - return { - 'my_server': { - 'public_key': my_server_info.get('public_key'), - 'clients': my_server_info.get('peers'), - }, - 'my_client': { - 'servers': my_client_servers, - }, - } diff --git a/plinth/modules/wireguard/forms.py b/plinth/modules/wireguard/forms.py index 9dc7ef977..3f03abb42 100644 --- a/plinth/modules/wireguard/forms.py +++ b/plinth/modules/wireguard/forms.py @@ -64,7 +64,7 @@ class AddServerForm(forms.Form): settings = { 'common': { 'type': 'wireguard', - 'zone': 'internal', + 'zone': 'external', }, 'ipv4': { 'method': 'manual', diff --git a/plinth/modules/wireguard/templates/wireguard.html b/plinth/modules/wireguard/templates/wireguard.html index 753836e44..8a85d2030 100644 --- a/plinth/modules/wireguard/templates/wireguard.html +++ b/plinth/modules/wireguard/templates/wireguard.html @@ -30,18 +30,22 @@ id="server-peers-list"> {% trans "Public Key" %} + {% trans "Allowed IPs" %} {% trans "Last Connected Time" %} - {% if server_peers %} - {% for peer in server_peers %} - - - - {{ peer.public_key }} - - - {{ peer.latest_handshake }} - + {% if server.peers %} + {% for peer in server.peers.values %} + {% if peer.public_key %} + + + + {{ peer.public_key }} + + + {{ peer.allowed_ips|join:", " }} + {{ peer.status.latest_handshake|default:'' }} + + {% endif %} {% endfor %} {% else %} @@ -54,6 +58,13 @@ {% endif %} +

+ {% blocktrans %} + Public key for this {{ box_name }}: + {% endblocktrans %} +

{{ server.public_key }}
+

+ @@ -72,15 +83,19 @@ {% if client_peers %} {% for interface, server in client_peers.items %} - - {{ server.peers.0.endpoint }} - - - {{ server.peers.0.public_key }} - - - {{ server.peers.0.status.latest_handshake }} - + {% for peer in server.peers.values %} + {% if forloop.first %} + + {{ peer.endpoint }} + + + {{ peer.public_key }} + + + {{ peer.status.latest_handshake|default:'' }} + + {% endif %} + {% endfor %} {% endfor %} {% else %} @@ -100,16 +115,6 @@ {% trans "Add Server" %} -

{% trans "Connection Information" %}

- -

- {% blocktrans %} - Public key for this {{ box_name }}: - {% endblocktrans %} - -

{{ public_key }}
-

- {{ block.super }} {% endblock %} diff --git a/plinth/modules/wireguard/templates/wireguard_show_client.html b/plinth/modules/wireguard/templates/wireguard_show_client.html index 416a87c6c..51fc9edd7 100644 --- a/plinth/modules/wireguard/templates/wireguard_show_client.html +++ b/plinth/modules/wireguard/templates/wireguard_show_client.html @@ -22,23 +22,58 @@ {% block content %} -

{{ title }}

+

{% trans "Connection Information" %}

+ + + + + + + + + + + + + + + + + + + + + + + +
{% trans "Client public key:" %}{{ client.public_key }}
{% trans "IP address to use:" %}{{ client.allowed_ips|join:", " }}
{% trans "Pre-shared key:" %}{{ client.preshared_key }}
{% trans "Server endpoints:" %} + {% for endpoint in endpoints %} +
{{ endpoint }}
+ {% endfor %} +
{% trans "Server's public key:" %}{{ server.public_key }}
-

{% trans "Connection Information" %}

-

{% trans "IP address to use:" %}

-

{% trans "Server endpoints:" %}

-

{% trans "Server's public key:" %} {{ my_server.public_key }}

-

{% trans "Pre-shared key:" %}

- -

{% trans "Status" %}

-

{% trans "Client Public Key:" %} {{ client.public_key }}

-

{% trans "Data transmitted:" %} {{ client.transfer_tx|filesizeformat }}

-

{% trans "Data received:" %} {{ client.transfer_rx|filesizeformat }}

-

{% trans "Latest handshake:" %} {{ client.latest_handshake }}

+

{% trans "Status" %}

+ + + + + + + + + + + + + + + +
{% trans "Data transmitted:" %}{{ client.status.transfer_tx|filesizeformat }}
{% trans "Data received:" %}{{ client.status.transfer_rx|filesizeformat }}
{% trans "Latest handshake:" %}{{ client.status.latest_handshake|default:'' }}

+ {% trans "Edit Client" %} - - {% trans "Endpoint:" %} - {{ server.peers.0.endpoint }} - - - {% trans "Public Key:" %} - {{ server.peers.0.public_key }} - - - {% trans "Pre-shared key:" %} - {{ server.peers.0.preshared_key }} - - - {% trans "Data transmitted:" %} - {{ server.peers.0.status.transfer_tx|filesizeformat }} - - - {% trans "Data received:" %} - {{ server.peers.0.status.transfer_rx|filesizeformat }} - - - {% trans "Latest handshake:" %} - {{ server.peers.0.status.latest_handshake }} - + {% for peer in server.peers.values %} + {% if forloop.first %} + + {% trans "Endpoint:" %} + {{ peer.endpoint }} + + + {% trans "Public Key:" %} + {{ peer.public_key }} + + + {% trans "Pre-shared key:" %} + {{ peer.preshared_key }} + + + {% trans "Data transmitted:" %} + {{ peer.status.transfer_tx|filesizeformat }} + + + {% trans "Data received:" %} + {{ peer.status.transfer_rx|filesizeformat }} + + + {% trans "Latest handshake:" %} + {{ peer.status.latest_handshake|default:'' }} + + {% endif %} + {% endfor %} diff --git a/plinth/modules/wireguard/utils.py b/plinth/modules/wireguard/utils.py index d41ba042e..86c404001 100644 --- a/plinth/modules/wireguard/utils.py +++ b/plinth/modules/wireguard/utils.py @@ -18,9 +18,100 @@ Utilities for managing WireGuard. """ +import datetime +import json import subprocess +import time -from plinth import network +from plinth import actions, network +from plinth.utils import import_from_gi + +nm = import_from_gi('NM', '1.0') + +IP_TEMPLATE = '10.84.0.{}' +WIREGUARD_SETTING = nm.SETTING_WIREGUARD_SETTING_NAME + + +def get_nm_info(): + """Get information from network manager.""" + client = network.get_nm_client() + + connections = {} + for connection in client.get_connections(): + if connection.get_connection_type() != WIREGUARD_SETTING: + continue + + settings = connection.get_setting_by_name(WIREGUARD_SETTING) + secrets = connection.get_secrets(WIREGUARD_SETTING) + connection.update_secrets(WIREGUARD_SETTING, secrets) + + info = {} + info['interface'] = connection.get_interface_name() + info['private_key'] = settings.get_private_key() + info['public_key'] = None + info['listen_port'] = settings.get_listen_port() + info['fwmark'] = settings.get_fwmark() + info['mtu'] = settings.get_mtu() + info['default_route'] = settings.get_ip4_auto_default_route() + info['peers'] = {} + for peer_index in range(settings.get_peers_len()): + peer = settings.get_peer(peer_index) + peer_info = { + 'endpoint': peer.get_endpoint(), + 'public_key': peer.get_public_key(), + 'preshared_key': peer.get_preshared_key(), + 'persistent_keepalive': peer.get_persistent_keepalive(), + 'allowed_ips': [] + } + for index in range(peer.get_allowed_ips_len()): + allowed_ip = peer.get_allowed_ip(index, None) + peer_info['allowed_ips'].append(allowed_ip) + + info['peers'][peer_info['public_key']] = peer_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() + + connections[info['interface']] = info + + return connections + + +def get_info(): + """Return server and clients info.""" + output = actions.superuser_run('wireguard', ['get-info']) + status = json.loads(output) + + nm_info = get_nm_info() + + my_server_info = None + my_client_servers = {} + for interface, info in nm_info.items(): + if interface == 'wg0': + my_server_info = info + else: + my_client_servers[interface] = info + + if interface not in status: + continue + + info['public_key'] = status[interface]['public_key'] + for status_peer in status[interface]['peers']: + if status_peer['latest_handshake']: + status_peer['latest_handshake'] = \ + datetime.datetime.fromtimestamp( + status_peer['latest_handshake']) + public_key = status_peer['public_key'] + info_peer = info['peers'].setdefault(public_key, {}) + info_peer['status'] = status_peer + + return { + 'my_server': my_server_info, + 'my_client': { + 'servers': my_client_servers, + }, + } def find_next_interface(): @@ -38,8 +129,106 @@ def find_next_interface(): def add_server(settings): - """Add a server.""" + """Add information for connecting to a server.""" interface_name = find_next_interface() - settings['common']['name'] = 'WireGuard-' + interface_name + settings['common']['name'] = 'WireGuard-Client-' + interface_name settings['common']['interface'] = interface_name network.add_connection(settings) + + +def setup_server(): + """Setup a server connection that clients can connect to.""" + process = subprocess.run(['wg', 'genkey'], check=True, capture_output=True) + private_key = process.stdout.decode().strip() + settings = { + 'common': { + 'name': 'WireGuard-Server-wg0', + 'type': WIREGUARD_SETTING, + 'zone': 'internal', + 'interface': 'wg0' + }, + 'ipv4': { + 'method': 'manual', + 'address': IP_TEMPLATE.format(1), + 'netmask': '255.255.255.0', + 'gateway': '', + 'dns': '', + 'second_dns': '', + }, + 'wireguard': { + 'private_key': private_key, + 'listen_port': 51820, + } + } + network.add_connection(settings) + + +def _get_next_available_ip_address(settings): + """Get the next available IP address to allocate to a client.""" + allocated_ips = set() + for peer_index in range(settings.get_peers_len()): + peer = settings.get_peer(peer_index) + for ip_index in range(peer.get_allowed_ips_len()): + allowed_ip = peer.get_allowed_ip(ip_index) + # We assume these are simple IP addresses but they can be subnets. + allocated_ips.add(allowed_ip) + + for index in range(2, 254): + ip_address = IP_TEMPLATE.format(index) + if ip_address not in allocated_ips: + return ip_address + + raise IndexError('Reached client limit') + + +def _server_connection(): + """Return a server connection. Create one if necessary.""" + connection = network.get_connection_by_interface_name('wg0') + if not connection: + setup_server() + + for _ in range(10): + # XXX: Improve this waiting by doing a synchronous D-Bus operation to + # add network manager connection instead. + time.sleep(1) + connection = network.get_connection_by_interface_name('wg0') + if connection: + break + + if not connection: + raise RuntimeError('Unable to create a server connection.') + + # Retrieve secrets so that when the connection is changed, secrets are + # preserved properly. + secrets = connection.get_secrets(WIREGUARD_SETTING) + connection.update_secrets(WIREGUARD_SETTING, secrets) + + return connection + + +def add_client(public_key): + """Add a permission for a client to connect our server.""" + connection = _server_connection() + settings = connection.get_setting_by_name(WIREGUARD_SETTING) + peer, _ = settings.get_peer_by_public_key(public_key) + if peer: + raise ValueError('Peer with public key already exists') + + peer = nm.WireGuardPeer.new() + peer.set_public_key(public_key, False) + peer.set_persistent_keepalive(25) # To keep NAT 'connections' alive + peer.append_allowed_ip(_get_next_available_ip_address(settings), False) + settings.append_peer(peer) + connection.commit_changes(True) + + +def remove_client(public_key): + """Remove permission for a client to connect our server.""" + connection = _server_connection() + settings = connection.get_setting_by_name(WIREGUARD_SETTING) + peer, peer_index = settings.get_peer_by_public_key(public_key) + if not peer: + raise KeyError('Client not found') + + settings.remove_peer(peer_index) + connection.commit_changes(True) diff --git a/plinth/modules/wireguard/views.py b/plinth/modules/wireguard/views.py index 1e1f72141..1558646a0 100644 --- a/plinth/modules/wireguard/views.py +++ b/plinth/modules/wireguard/views.py @@ -29,7 +29,8 @@ from django.utils.translation import ugettext as _ from django.views.generic import FormView, TemplateView import plinth.modules.wireguard as wireguard -from plinth import actions, network +from plinth import network +from plinth.modules.names.components import DomainName from plinth.views import AppView from . import forms, utils @@ -49,9 +50,8 @@ class WireguardView(AppView): def get_context_data(self, **kwargs): """Return additional context for rendering the template.""" context = super().get_context_data(**kwargs) - context['public_key'] = wireguard.get_public_key() - info = wireguard.get_info() - context['server_peers'] = info['my_server']['clients'] + info = utils.get_info() + context['server'] = info['my_server'] context['client_peers'] = info['my_client']['servers'] return context @@ -72,7 +72,13 @@ class AddClientView(SuccessMessageMixin, FormView): def form_valid(self, form): """Add the client.""" public_key = form.cleaned_data.get('public_key') - actions.superuser_run('wireguard', ['add-client', public_key]) + try: + utils.add_client(public_key) + except ValueError: + messages.warning(self.request, + _('Client with public key already exists')) + return redirect('wireguard:index') + return super().form_valid(form) @@ -86,12 +92,17 @@ class ShowClientView(SuccessMessageMixin, TemplateView): context['title'] = _('Show Client') public_key = urllib.parse.unquote(self.kwargs['public_key']) - info = wireguard.get_info() - context.update(info) - for client in info['my_server']['clients']: - if client['public_key'] == public_key: - context['client'] = client + server_info = utils.get_info()['my_server'] + if not server_info or public_key not in server_info['peers']: + raise Http404 + domains = DomainName.list_names(filter_for_service='wireguard') + context['server'] = server_info + context['client'] = server_info['peers'][public_key] + context['endpoints'] = [ + domain + ':' + str(server_info['listen_port']) + for domain in domains + ] return context @@ -117,10 +128,17 @@ class EditClientView(SuccessMessageMixin, FormView): def form_valid(self, form): """Update the client.""" old_public_key = form.initial['public_key'] - actions.superuser_run('wireguard', ['remove-client', old_public_key]) - public_key = form.cleaned_data.get('public_key') - actions.superuser_run('wireguard', ['add-client', public_key]) + + if old_public_key != public_key: + try: + utils.add_client(public_key) + except ValueError: + messages.warning(self.request, + _('Client with public key already exists')) + + utils.remove_client(old_public_key) + return super().form_valid(form) @@ -138,8 +156,12 @@ class DeleteClientView(SuccessMessageMixin, TemplateView): def post(self, request, public_key): """Delete the client.""" public_key = urllib.parse.unquote(public_key) - actions.superuser_run('wireguard', ['remove-client', public_key]) - messages.success(request, _('Client deleted.')) + try: + utils.remove_client(public_key) + messages.success(request, _('Client deleted.')) + except KeyError: + messages.error(request, _('Client not found')) + return redirect('wireguard:index') @@ -172,7 +194,7 @@ class ShowServerView(SuccessMessageMixin, TemplateView): context['title'] = _('Server Information') interface = self.kwargs['interface'] - info = wireguard.get_info() + info = utils.get_info() server = info['my_client']['servers'].get(interface) if not server: raise Http404 @@ -199,14 +221,14 @@ class EditServerView(SuccessMessageMixin, FormView): """Get initial form data.""" initial = super().get_initial() interface = self.kwargs['interface'] - info = wireguard.get_nm_info() + info = utils.get_nm_info() server = info.get(interface) if not server: raise Http404 initial['ip_address'] = server.get('ip_address') if server['peers']: - peer = server['peers'][0] + peer = next(peer for peer in server['peers'].values()) initial['peer_endpoint'] = peer['endpoint'] initial['peer_public_key'] = peer['public_key'] initial['private_key'] = server['private_key'] @@ -220,7 +242,7 @@ class EditServerView(SuccessMessageMixin, FormView): settings = form.get_settings() interface = self.kwargs['interface'] settings['common']['interface'] = interface - settings['common']['name'] = 'WireGuard-' + interface + settings['common']['name'] = 'WireGuard-Client-' + interface connection = network.get_connection_by_interface_name(interface) network.edit_connection(connection, settings) return super().form_valid(form) @@ -236,14 +258,14 @@ class DeleteServerView(SuccessMessageMixin, TemplateView): context['title'] = _('Delete Server') interface = self.kwargs['interface'] - info = wireguard.get_nm_info() + info = utils.get_nm_info() server = info.get(interface) if not server: raise Http404 context['interface'] = interface if server['peers']: - peer = server['peers'][0] + peer = next(peer for peer in server['peers'].values()) context['peer_endpoint'] = peer['endpoint'] context['peer_public_key'] = peer['public_key'] diff --git a/plinth/network.py b/plinth/network.py index d491e3083..7d3add55c 100644 --- a/plinth/network.py +++ b/plinth/network.py @@ -477,19 +477,27 @@ def _update_wireguard_settings(connection, wireguard): settings.set_property(nm.SETTING_WIREGUARD_PRIVATE_KEY, wireguard['private_key']) - peer = nm.WireGuardPeer.new() - peer.set_endpoint(wireguard['peer_endpoint'], False) - peer.set_public_key(wireguard['peer_public_key'], False) - if wireguard['preshared_key']: - # Flag NONE means that NM should store and retain the secret. - # Default seems to be NOT_REQUIRED in this case. - peer.set_preshared_key_flags(nm.SettingSecretFlags.NONE) - peer.set_preshared_key(wireguard['preshared_key'], False) + if 'listen_port' in wireguard: + settings.set_property(nm.SETTING_WIREGUARD_LISTEN_PORT, + wireguard['listen_port']) - peer.append_allowed_ip('0.0.0.0/0', False) - peer.append_allowed_ip('::/0', False) - settings.clear_peers() - settings.append_peer(peer) + if 'peer_public_key' in wireguard: + peer = nm.WireGuardPeer.new() + peer.set_public_key(wireguard['peer_public_key'], False) + + if 'peer_endpoint' in wireguard: + peer.set_endpoint(wireguard['peer_endpoint'], False) + + if wireguard['preshared_key']: + # Flag NONE means that NM should store and retain the secret. + # Default seems to be NOT_REQUIRED in this case. + peer.set_preshared_key_flags(nm.SettingSecretFlags.NONE) + peer.set_preshared_key(wireguard['preshared_key'], False) + + peer.append_allowed_ip('0.0.0.0/0', False) + peer.append_allowed_ip('::/0', False) + settings.clear_peers() + settings.append_peer(peer) def _update_settings(connection, connection_uuid, settings):