diff --git a/actions/wireguard b/actions/wireguard
index 76fc529ce..1231bf1c8 100755
--- a/actions/wireguard
+++ b/actions/wireguard
@@ -34,6 +34,10 @@ 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()
@@ -62,6 +66,19 @@ def parse_arguments():
'--all-outgoing', action='store_true',
help='Use this connection to send all outgoing traffic')
+ 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)
@@ -216,13 +233,57 @@ def subcommand_add_server(arguments):
subprocess.run(args, check=True)
+def subcommand_modify_server(arguments):
+ """Modify a server."""
+ interfaces = _get_info()
+ interfaces.pop(SERVER_INTERFACE)
+ interface_to_modify = None
+ for interface in interfaces.values():
+ if interface['peers']:
+ peer = interface['peers'][0]
+ if peer['public_key'] == arguments.public_key:
+ interface_to_modify = interface['interface_name']
+
+ if interface_to_modify:
+ args = ['wg', 'set', interface_to_modify, 'peer', arguments.public_key]
+ if arguments.pre_shared_key:
+ args += ['preshared-key', arguments.pre_shared_key]
+
+ args += ['endpoint', arguments.endpoint]
+ subprocess.run(args, check=True)
+
+ subprocess.run(['nmcli', 'con', 'modify',
+ 'WireGuard-' + interface_to_modify,
+ 'ipv4.method', 'manual',
+ 'ipv4.addresses', arguments.client_ip + '/24'],
+ check=True)
+
+ else:
+ raise InterfaceNotFoundError(
+ 'Interface with peer %s not found' % arguments.publickey)
+
+
def subcommand_remove_server(arguments):
"""Remove a server."""
- # XXX: fix this
- subprocess.run(
- ['wg', 'set', SERVER_INTERFACE, 'peer', arguments.publickey, 'remove'],
- check=True)
- # TODO: also delete NM connection
+ interfaces = _get_info()
+ interfaces.pop(SERVER_INTERFACE)
+ interface_to_remove = None
+ for interface in interfaces.values():
+ if interface['peers']:
+ peer = interface['peers'][0]
+ if peer['public_key'] == arguments.publickey:
+ interface_to_remove = interface['interface_name']
+
+ if interface_to_remove:
+ subprocess.run(
+ ['nmcli', 'con', 'delete', 'WireGuard-' + interface_to_remove],
+ check=True)
+ subprocess.run(['ip', 'link', 'delete', interface_to_remove],
+ check=True)
+
+ else:
+ raise InterfaceNotFoundError(
+ 'Interface with peer %s not found' % arguments.publickey)
def main():
diff --git a/plinth/modules/wireguard/__init__.py b/plinth/modules/wireguard/__init__.py
index 9f830e77c..fc02e46ad 100644
--- a/plinth/modules/wireguard/__init__.py
+++ b/plinth/modules/wireguard/__init__.py
@@ -122,8 +122,11 @@ def get_info():
output = actions.superuser_run('wireguard', ['get-info'])
info = json.loads(output)
my_server_info = info.pop(SERVER_INTERFACE)
- my_client_servers = [interface['peers'] and interface['peers'][0] or {}
- for interface in info.values()]
+ my_client_servers = []
+ for interface in info.values():
+ if interface['peers']:
+ my_client_servers.append(interface['peers'][0])
+
return {
'my_server': {
'public_key': my_server_info['public_key'],
diff --git a/plinth/modules/wireguard/templates/wireguard.html b/plinth/modules/wireguard/templates/wireguard.html
index 13a2132ad..7734d7f47 100644
--- a/plinth/modules/wireguard/templates/wireguard.html
+++ b/plinth/modules/wireguard/templates/wireguard.html
@@ -31,7 +31,6 @@
| {% trans "Public Key" %} |
{% trans "Last Connected Time" %} |
- {% trans "Delete" %} |
{% if server_peers %}
{% for peer in server_peers %}
@@ -42,11 +41,6 @@
{{ peer.latest_handshake }} |
-
-
-
- |
{% endfor %}
@@ -76,7 +70,6 @@
{% trans "Endpoint" %} |
{% trans "Public Key" %} |
{% trans "Last Connected Time" %} |
- {% trans "Edit" %} |
{% if client_peers %}
{% for peer in client_peers %}
@@ -88,7 +81,6 @@
{{ peer.latest_handshake }} |
- Edit |
{% endfor %}
diff --git a/plinth/modules/wireguard/templates/wireguard_delete_server.html b/plinth/modules/wireguard/templates/wireguard_delete_server.html
new file mode 100644
index 000000000..1fb708300
--- /dev/null
+++ b/plinth/modules/wireguard/templates/wireguard_delete_server.html
@@ -0,0 +1,42 @@
+{% extends "base.html" %}
+{% comment %}
+#
+# This file is part of FreedomBox.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+{% endcomment %}
+
+{% load bootstrap %}
+{% load i18n %}
+
+{% block content %}
+
+ {{ title }}
+
+
+ {% trans "Are you sure that you want to delete this server?" %}
+
+
+ {{ public_key }}
+
+
+
+
+{% endblock %}
diff --git a/plinth/modules/wireguard/templates/wireguard_edit_client.html b/plinth/modules/wireguard/templates/wireguard_edit_client.html
new file mode 100644
index 000000000..93da05b41
--- /dev/null
+++ b/plinth/modules/wireguard/templates/wireguard_edit_client.html
@@ -0,0 +1,37 @@
+{% extends "base.html" %}
+{% comment %}
+#
+# This file is part of FreedomBox.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+{% endcomment %}
+
+{% load bootstrap %}
+{% load i18n %}
+
+{% block content %}
+
+ {{ title }}
+
+
+
+{% endblock %}
diff --git a/plinth/modules/wireguard/templates/wireguard_edit_server.html b/plinth/modules/wireguard/templates/wireguard_edit_server.html
new file mode 100644
index 000000000..b2f1a6e6c
--- /dev/null
+++ b/plinth/modules/wireguard/templates/wireguard_edit_server.html
@@ -0,0 +1,37 @@
+{% extends "base.html" %}
+{% comment %}
+#
+# This file is part of FreedomBox.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+{% endcomment %}
+
+{% load bootstrap %}
+{% load i18n %}
+
+{% block content %}
+
+ {{ title }}
+
+
+
+{% endblock %}
diff --git a/plinth/modules/wireguard/templates/wireguard_show_client.html b/plinth/modules/wireguard/templates/wireguard_show_client.html
index e07238ca9..32718245d 100644
--- a/plinth/modules/wireguard/templates/wireguard_show_client.html
+++ b/plinth/modules/wireguard/templates/wireguard_show_client.html
@@ -36,4 +36,16 @@
{% trans "Data received:" %} {{ client.transfer_rx }}
{% trans "Latest handshake:" %} {{ client.latest_handshake }}
+
+
+ {% trans "Edit Client" %}
+
+
+
+ {% trans "Delete Client" %}
+
+
+
{% endblock %}
diff --git a/plinth/modules/wireguard/templates/wireguard_show_server.html b/plinth/modules/wireguard/templates/wireguard_show_server.html
index 9ad01eaed..95997a429 100644
--- a/plinth/modules/wireguard/templates/wireguard_show_server.html
+++ b/plinth/modules/wireguard/templates/wireguard_show_server.html
@@ -32,4 +32,16 @@
{% trans "Data received:" %} {{ server.transfer_rx }}
{% trans "Latest handshake:" %} {{ server.latest_handshake }}
+
+
+ {% trans "Edit Server" %}
+
+
+
+ {% trans "Delete Server" %}
+
+
+
{% endblock %}
diff --git a/plinth/modules/wireguard/urls.py b/plinth/modules/wireguard/urls.py
index fa2a1f159..20c9c11d6 100644
--- a/plinth/modules/wireguard/urls.py
+++ b/plinth/modules/wireguard/urls.py
@@ -28,10 +28,16 @@ urlpatterns = [
name='add-client'),
url(r'^apps/wireguard/client/(?P[^/]+)/show/$',
views.ShowClientView.as_view(), name='show-client'),
+ url(r'^apps/wireguard/client/(?P[^/]+)/edit/$',
+ views.EditClientView.as_view(), name='edit-client'),
url(r'^apps/wireguard/client/(?P[^/]+)/delete/$',
views.DeleteClientView.as_view(), name='delete-client'),
url(r'^apps/wireguard/server/add/$', views.AddServerView.as_view(),
name='add-server'),
url(r'^apps/wireguard/server/(?P[^/]+)/show/$',
views.ShowServerView.as_view(), name='show-server'),
+ url(r'^apps/wireguard/server/(?P[^/]+)/edit/$',
+ views.EditServerView.as_view(), name='edit-server'),
+ url(r'^apps/wireguard/server/(?P[^/]+)/delete/$',
+ views.DeleteServerView.as_view(), name='delete-server'),
]
diff --git a/plinth/modules/wireguard/views.py b/plinth/modules/wireguard/views.py
index 61e89ecfd..c919a5b88 100644
--- a/plinth/modules/wireguard/views.py
+++ b/plinth/modules/wireguard/views.py
@@ -94,6 +94,35 @@ class ShowClientView(SuccessMessageMixin, TemplateView):
return context
+class EditClientView(SuccessMessageMixin, FormView):
+ """View to modify a client."""
+ form_class = forms.AddClientForm
+ template_name = 'wireguard_edit_client.html'
+ success_url = reverse_lazy('wireguard:index')
+ success_message = _('Updated client.')
+
+ def get_context_data(self, **kwargs):
+ """Return additional context for rendering the template."""
+ context = super().get_context_data(**kwargs)
+ context['title'] = _('Modify Client')
+ return context
+
+ def get_initial(self):
+ """Get initial form data."""
+ initial = super().get_initial()
+ initial['public_key'] = urllib.parse.unquote(self.kwargs['public_key'])
+ return initial
+
+ 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])
+ return super().form_valid(form)
+
+
class DeleteClientView(SuccessMessageMixin, TemplateView):
"""View to delete a client."""
template_name = 'wireguard_delete_client.html'
@@ -163,3 +192,75 @@ class ShowServerView(SuccessMessageMixin, TemplateView):
context['server'] = server
return context
+
+
+class EditServerView(SuccessMessageMixin, FormView):
+ """View to modify a server."""
+ form_class = forms.AddServerForm
+ template_name = 'wireguard_edit_server.html'
+ success_url = reverse_lazy('wireguard:index')
+ success_message = _('Updated server.')
+
+ def get_context_data(self, **kwargs):
+ """Return additional context for rendering the template."""
+ context = super().get_context_data(**kwargs)
+ context['title'] = _('Modify Server')
+ return context
+
+ def get_initial(self):
+ """Get initial form data."""
+ initial = super().get_initial()
+ public_key = urllib.parse.unquote(self.kwargs['public_key'])
+ info = wireguard.get_info()
+ for server in info['my_client']['servers']:
+ if server['public_key'] == public_key:
+ initial['endpoint'] = server['endpoint']
+ initial['client_ip_address'] = ''
+ initial['public_key'] = server['public_key']
+ pre_shared_key = server['preshared_key']
+ if pre_shared_key == '(none)':
+ initial['pre_shared_key'] = ''
+ else:
+ initial['pre_shared_key'] = server['preshared_key']
+
+ initial['all_outgoing_traffic'] = False
+
+ return initial
+
+ def form_valid(self, form):
+ """Update the server."""
+ endpoint = form.cleaned_data.get('endpoint')
+ client_ip_address = form.cleaned_data.get('client_ip_address')
+ public_key = form.cleaned_data.get('public_key')
+ pre_shared_key = form.cleaned_data.get('pre_shared_key')
+ all_outgoing_traffic = form.cleaned_data.get('all_outgoing_traffic')
+ args = ['modify-server', '--endpoint', endpoint, '--client-ip',
+ client_ip_address, '--public-key', public_key]
+ if pre_shared_key:
+ # TODO: pass pre-shared key through stdin
+ args += ['--pre-shared-key', pre_shared_key]
+
+ if all_outgoing_traffic:
+ args.append('--all-outgoing')
+
+ actions.superuser_run('wireguard', args)
+ return super().form_valid(form)
+
+
+class DeleteServerView(SuccessMessageMixin, TemplateView):
+ """View to delete a server."""
+ template_name = 'wireguard_delete_server.html'
+
+ def get_context_data(self, **kwargs):
+ """Return additional context data for rendering the template."""
+ context = super().get_context_data(**kwargs)
+ context['title'] = _('Delete Server')
+ context['public_key'] = urllib.parse.unquote(self.kwargs['public_key'])
+ return context
+
+ def post(self, request, public_key):
+ """Delete the server."""
+ public_key = urllib.parse.unquote(public_key)
+ actions.superuser_run('wireguard', ['remove-server', public_key])
+ messages.success(request, _('Server deleted.'))
+ return redirect('wireguard:index')