mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-09-19 04:59:01 +00:00
wireguard: Add views for editing and deleting clients and servers
Signed-off-by: James Valleroy <jvalleroy@mailbox.org> Reviewed-by: Sunil Mohan Adapa <sunil@medhas.org>
This commit is contained in:
parent
9eb546df26
commit
aa66a9135c
@ -34,6 +34,10 @@ PRIVATE_KEY_PATH = KEY_FOLDER / 'privatekey'
|
|||||||
PUBLIC_KEY_PATH = KEY_FOLDER / 'publickey'
|
PUBLIC_KEY_PATH = KEY_FOLDER / 'publickey'
|
||||||
|
|
||||||
|
|
||||||
|
class InterfaceNotFoundError(Exception):
|
||||||
|
"""Exception raised when no matching interface is found."""
|
||||||
|
|
||||||
|
|
||||||
def parse_arguments():
|
def parse_arguments():
|
||||||
"""Return parsed command line arguments as dictionary."""
|
"""Return parsed command line arguments as dictionary."""
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
@ -62,6 +66,19 @@ def parse_arguments():
|
|||||||
'--all-outgoing', action='store_true',
|
'--all-outgoing', action='store_true',
|
||||||
help='Use this connection to send all outgoing traffic')
|
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',
|
remove_server = subparsers.add_parser('remove-server',
|
||||||
help='Remove a server')
|
help='Remove a server')
|
||||||
remove_server.add_argument('publickey', help=PUBLIC_KEY_HELP)
|
remove_server.add_argument('publickey', help=PUBLIC_KEY_HELP)
|
||||||
@ -216,13 +233,57 @@ def subcommand_add_server(arguments):
|
|||||||
subprocess.run(args, check=True)
|
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):
|
def subcommand_remove_server(arguments):
|
||||||
"""Remove a server."""
|
"""Remove a server."""
|
||||||
# XXX: fix this
|
interfaces = _get_info()
|
||||||
subprocess.run(
|
interfaces.pop(SERVER_INTERFACE)
|
||||||
['wg', 'set', SERVER_INTERFACE, 'peer', arguments.publickey, 'remove'],
|
interface_to_remove = None
|
||||||
check=True)
|
for interface in interfaces.values():
|
||||||
# TODO: also delete NM connection
|
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():
|
def main():
|
||||||
|
|||||||
@ -122,8 +122,11 @@ def get_info():
|
|||||||
output = actions.superuser_run('wireguard', ['get-info'])
|
output = actions.superuser_run('wireguard', ['get-info'])
|
||||||
info = json.loads(output)
|
info = json.loads(output)
|
||||||
my_server_info = info.pop(SERVER_INTERFACE)
|
my_server_info = info.pop(SERVER_INTERFACE)
|
||||||
my_client_servers = [interface['peers'] and interface['peers'][0] or {}
|
my_client_servers = []
|
||||||
for interface in info.values()]
|
for interface in info.values():
|
||||||
|
if interface['peers']:
|
||||||
|
my_client_servers.append(interface['peers'][0])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'my_server': {
|
'my_server': {
|
||||||
'public_key': my_server_info['public_key'],
|
'public_key': my_server_info['public_key'],
|
||||||
|
|||||||
@ -31,7 +31,6 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Public Key" %}</th>
|
<th>{% trans "Public Key" %}</th>
|
||||||
<th>{% trans "Last Connected Time" %}</th>
|
<th>{% trans "Last Connected Time" %}</th>
|
||||||
<th>{% trans "Delete" %}</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
{% if server_peers %}
|
{% if server_peers %}
|
||||||
{% for peer in server_peers %}
|
{% for peer in server_peers %}
|
||||||
@ -42,11 +41,6 @@
|
|||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ peer.latest_handshake }}</td>
|
<td>{{ peer.latest_handshake }}</td>
|
||||||
<td><a class="btn btn-sm btn-default"
|
|
||||||
href="{% url 'wireguard:delete-client' peer.public_key|urlencode:'' %}">
|
|
||||||
<span class="fa fa-trash-o" aria-hidden="true">
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
@ -76,7 +70,6 @@
|
|||||||
<th>{% trans "Endpoint" %}</th>
|
<th>{% trans "Endpoint" %}</th>
|
||||||
<th>{% trans "Public Key" %}</th>
|
<th>{% trans "Public Key" %}</th>
|
||||||
<th>{% trans "Last Connected Time" %}</th>
|
<th>{% trans "Last Connected Time" %}</th>
|
||||||
<th>{% trans "Edit" %}</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
{% if client_peers %}
|
{% if client_peers %}
|
||||||
{% for peer in client_peers %}
|
{% for peer in client_peers %}
|
||||||
@ -88,7 +81,6 @@
|
|||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ peer.latest_handshake }}</td>
|
<td>{{ peer.latest_handshake }}</td>
|
||||||
<td>Edit</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
|||||||
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
{% load bootstrap %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<h3>{{ title }}</h3>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
{% trans "Are you sure that you want to delete this server?" %}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<b>{{ public_key }}</b>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<form class="form" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
|
<input type="submit" class="btn btn-danger"
|
||||||
|
value="{% trans "Delete Server" %}"/>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
{% load bootstrap %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<h3>{{ title }}</h3>
|
||||||
|
|
||||||
|
<form class="form" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
|
{{ form|bootstrap }}
|
||||||
|
|
||||||
|
<input type="submit" class="btn btn-primary"
|
||||||
|
value="{% trans "Update Client" %}"/>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
{% load bootstrap %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<h3>{{ title }}</h3>
|
||||||
|
|
||||||
|
<form class="form" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
|
{{ form|bootstrap }}
|
||||||
|
|
||||||
|
<input type="submit" class="btn btn-primary"
|
||||||
|
value="{% trans "Update Server" %}"/>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@ -36,4 +36,16 @@
|
|||||||
<p>{% trans "Data received:" %} {{ client.transfer_rx }}</p>
|
<p>{% trans "Data received:" %} {{ client.transfer_rx }}</p>
|
||||||
<p>{% trans "Latest handshake:" %} {{ client.latest_handshake }}</p>
|
<p>{% trans "Latest handshake:" %} {{ client.latest_handshake }}</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a class="btn btn-sm btn-default"
|
||||||
|
href="{% url 'wireguard:edit-client' client.public_key|urlencode:'' %}">
|
||||||
|
{% trans "Edit Client" %}
|
||||||
|
</a>
|
||||||
|
<a class="btn btn-sm btn-default"
|
||||||
|
href="{% url 'wireguard:delete-client' client.public_key|urlencode:'' %}">
|
||||||
|
<span class="fa fa-trash-o" aria-hidden="true"></span>
|
||||||
|
{% trans "Delete Client" %}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@ -32,4 +32,16 @@
|
|||||||
<p>{% trans "Data received:" %} {{ server.transfer_rx }}</p>
|
<p>{% trans "Data received:" %} {{ server.transfer_rx }}</p>
|
||||||
<p>{% trans "Latest handshake:" %} {{ server.latest_handshake }}</p>
|
<p>{% trans "Latest handshake:" %} {{ server.latest_handshake }}</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a class="btn btn-sm btn-default"
|
||||||
|
href="{% url 'wireguard:edit-server' server.public_key|urlencode:'' %}">
|
||||||
|
{% trans "Edit Server" %}
|
||||||
|
</a>
|
||||||
|
<a class="btn btn-sm btn-default"
|
||||||
|
href="{% url 'wireguard:delete-server' server.public_key|urlencode:'' %}">
|
||||||
|
<span class="fa fa-trash-o" aria-hidden="true"></span>
|
||||||
|
{% trans "Delete Server" %}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@ -28,10 +28,16 @@ urlpatterns = [
|
|||||||
name='add-client'),
|
name='add-client'),
|
||||||
url(r'^apps/wireguard/client/(?P<public_key>[^/]+)/show/$',
|
url(r'^apps/wireguard/client/(?P<public_key>[^/]+)/show/$',
|
||||||
views.ShowClientView.as_view(), name='show-client'),
|
views.ShowClientView.as_view(), name='show-client'),
|
||||||
|
url(r'^apps/wireguard/client/(?P<public_key>[^/]+)/edit/$',
|
||||||
|
views.EditClientView.as_view(), name='edit-client'),
|
||||||
url(r'^apps/wireguard/client/(?P<public_key>[^/]+)/delete/$',
|
url(r'^apps/wireguard/client/(?P<public_key>[^/]+)/delete/$',
|
||||||
views.DeleteClientView.as_view(), name='delete-client'),
|
views.DeleteClientView.as_view(), name='delete-client'),
|
||||||
url(r'^apps/wireguard/server/add/$', views.AddServerView.as_view(),
|
url(r'^apps/wireguard/server/add/$', views.AddServerView.as_view(),
|
||||||
name='add-server'),
|
name='add-server'),
|
||||||
url(r'^apps/wireguard/server/(?P<public_key>[^/]+)/show/$',
|
url(r'^apps/wireguard/server/(?P<public_key>[^/]+)/show/$',
|
||||||
views.ShowServerView.as_view(), name='show-server'),
|
views.ShowServerView.as_view(), name='show-server'),
|
||||||
|
url(r'^apps/wireguard/server/(?P<public_key>[^/]+)/edit/$',
|
||||||
|
views.EditServerView.as_view(), name='edit-server'),
|
||||||
|
url(r'^apps/wireguard/server/(?P<public_key>[^/]+)/delete/$',
|
||||||
|
views.DeleteServerView.as_view(), name='delete-server'),
|
||||||
]
|
]
|
||||||
|
|||||||
@ -94,6 +94,35 @@ class ShowClientView(SuccessMessageMixin, TemplateView):
|
|||||||
return context
|
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):
|
class DeleteClientView(SuccessMessageMixin, TemplateView):
|
||||||
"""View to delete a client."""
|
"""View to delete a client."""
|
||||||
template_name = 'wireguard_delete_client.html'
|
template_name = 'wireguard_delete_client.html'
|
||||||
@ -163,3 +192,75 @@ class ShowServerView(SuccessMessageMixin, TemplateView):
|
|||||||
context['server'] = server
|
context['server'] = server
|
||||||
|
|
||||||
return context
|
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')
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user