mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-04-22 10:01:45 +00:00
openvpn: New module VPN into FreedomBox
- Authentication using client certificates. Extra password based authentication for later. - Auto setup of CA, server and client certificates. - Provides a .ovpn profile for each user for easy setup. - Use 4096 bit Diffie-Hellman parameters for better security. If this takes to much time, reduce it to 2048 or 1024, at least during debugging.
This commit is contained in:
parent
52bfb475e8
commit
646763ff3c
211
actions/openvpn
Executable file
211
actions/openvpn
Executable file
@ -0,0 +1,211 @@
|
||||
#!/usr/bin/python3
|
||||
# -*- mode: python -*-
|
||||
#
|
||||
# This file is part of Plinth.
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
"""
|
||||
Configuration helper for OpenVPN server.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from plinth import action_utils
|
||||
|
||||
KEYS_DIRECTORY = '/etc/openvpn/freedombox-keys'
|
||||
|
||||
DH_KEY = '/etc/openvpn/freedombox-keys/dh4096.pem'
|
||||
|
||||
SERVER_CONFIGURATION_PATH = '/etc/openvpn/freedombox.conf'
|
||||
|
||||
CA_CERTIFICATE_PATH = KEYS_DIRECTORY + '/ca.crt'
|
||||
USER_CERTIFICATE_PATH = KEYS_DIRECTORY + '/{username}.crt'
|
||||
USER_KEY_PATH = KEYS_DIRECTORY + '/{username}.key'
|
||||
|
||||
SERVER_CONFIGURATION = '''
|
||||
port 1194
|
||||
proto udp
|
||||
dev tun
|
||||
ca /etc/openvpn/freedombox-keys/ca.crt
|
||||
cert /etc/openvpn/freedombox-keys/server.crt
|
||||
key /etc/openvpn/freedombox-keys/server.key
|
||||
dh /etc/openvpn/freedombox-keys/dh4096.pem
|
||||
server 10.91.0.0 255.255.255.0
|
||||
keepalive 10 120
|
||||
cipher AES-256-CBC
|
||||
comp-lzo
|
||||
verb 3
|
||||
'''
|
||||
|
||||
CLIENT_CONFIGURATION = '''
|
||||
client
|
||||
remote {remote} 1194
|
||||
proto udp
|
||||
dev tun
|
||||
nobind
|
||||
remote-cert-tls server
|
||||
cipher AES-256-CBC
|
||||
comp-lzo
|
||||
redirect-gateway
|
||||
verb 3
|
||||
<ca>
|
||||
{ca}</ca>
|
||||
<cert>
|
||||
{cert}</cert>
|
||||
<key>
|
||||
{key}</key>'''
|
||||
|
||||
CERTIFICATE_CONFIGURATION = {
|
||||
'KEY_CONFIG': '/usr/share/easy-rsa/openssl-1.0.0.cnf',
|
||||
'KEY_DIR': KEYS_DIRECTORY,
|
||||
'OPENSSL': 'openssl',
|
||||
'KEY_SIZE': '4096',
|
||||
'CA_EXPIRE': '3650',
|
||||
'KEY_EXPIRE': '3650',
|
||||
'KEY_COUNTRY': 'US',
|
||||
'KEY_PROVINCE': 'NY',
|
||||
'KEY_CITY': 'New York',
|
||||
'KEY_ORG': 'FreedomBox',
|
||||
'KEY_EMAIL': 'me@freedombox',
|
||||
'KEY_OU': 'Home',
|
||||
'KEY_NAME': 'FreedomBox'
|
||||
}
|
||||
|
||||
COMMON_ARGS = {'env': CERTIFICATE_CONFIGURATION,
|
||||
'cwd': KEYS_DIRECTORY}
|
||||
|
||||
|
||||
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('is-setup', help='Return whether setup is completed')
|
||||
subparsers.add_parser('setup', help='Setup OpenVPN server configuration')
|
||||
|
||||
subparsers.add_parser('enable', help='Enable OpenVPN server')
|
||||
subparsers.add_parser('disable', help='Disable OpenVPN server')
|
||||
|
||||
get_profile = subparsers.add_parser(
|
||||
'get-profile', help='Return the OpenVPN profile of a user')
|
||||
get_profile.add_argument('username', help='User to get profile for')
|
||||
get_profile.add_argument('remote_server',
|
||||
help='The server name for the user to connect')
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def subcommand_is_setup(_):
|
||||
"""Return whether setup is complete."""
|
||||
print('true' if os.path.isfile(DH_KEY) else 'false')
|
||||
|
||||
|
||||
def subcommand_setup(_):
|
||||
"""Setup configuration, CA and certificates."""
|
||||
_create_server_config()
|
||||
_create_certificates()
|
||||
_setup_firewall()
|
||||
action_utils.service_enable('openvpn@freedombox')
|
||||
action_utils.service_restart('openvpn@freedombox')
|
||||
|
||||
|
||||
def _create_server_config():
|
||||
"""Write server configuration."""
|
||||
if os.path.exists(SERVER_CONFIGURATION_PATH):
|
||||
return
|
||||
|
||||
with open(SERVER_CONFIGURATION_PATH, 'w') as file_handle:
|
||||
file_handle.write(SERVER_CONFIGURATION)
|
||||
|
||||
|
||||
def _setup_firewall():
|
||||
"""Add TUN device to internal zone in firewalld."""
|
||||
subprocess.call(['firewall-cmd', '--zone', 'internal',
|
||||
'--add-interface', 'tun+'])
|
||||
subprocess.call(['firewall-cmd', '--permanent', '--zone', 'internal',
|
||||
'--add-interface', 'tun+'])
|
||||
|
||||
|
||||
def _create_certificates():
|
||||
"""Generate CA and server certificates."""
|
||||
try:
|
||||
os.mkdir(KEYS_DIRECTORY, 0o700)
|
||||
except FileExistsError:
|
||||
pass
|
||||
|
||||
subprocess.check_call(['/usr/share/easy-rsa/clean-all'], **COMMON_ARGS)
|
||||
subprocess.check_call(['/usr/share/easy-rsa/pkitool', '--initca'],
|
||||
**COMMON_ARGS)
|
||||
subprocess.check_call(['/usr/share/easy-rsa/pkitool', '--server', 'server'],
|
||||
**COMMON_ARGS)
|
||||
subprocess.check_call(['/usr/share/easy-rsa/build-dh'], **COMMON_ARGS)
|
||||
|
||||
|
||||
def subcommand_enable(_):
|
||||
"""Start OpenVPN service."""
|
||||
action_utils.service_enable('openvpn@freedombox')
|
||||
|
||||
|
||||
def subcommand_disable(_):
|
||||
"""Stop OpenVPN service."""
|
||||
action_utils.service_disable('openvpn@freedombox')
|
||||
|
||||
|
||||
def subcommand_get_profile(arguments):
|
||||
"""Return the profile for a user."""
|
||||
username = arguments.username
|
||||
remote_server = arguments.remote_server
|
||||
|
||||
if username == 'ca' or username == 'server':
|
||||
raise Exception('Invalid username')
|
||||
|
||||
user_certificate = USER_CERTIFICATE_PATH.format(username=username)
|
||||
user_key = USER_KEY_PATH.format(username=username)
|
||||
|
||||
if not os.path.isfile(user_certificate) or not os.path.isfile(user_key):
|
||||
subprocess.check_call(['/usr/share/easy-rsa/pkitool', username],
|
||||
**COMMON_ARGS)
|
||||
|
||||
user_certificate_string = _read_file(user_certificate)
|
||||
user_key_string = _read_file(user_key)
|
||||
ca_string = _read_file(CA_CERTIFICATE_PATH)
|
||||
|
||||
profile = CLIENT_CONFIGURATION.format(
|
||||
ca=ca_string, cert=user_certificate_string, key=user_key_string,
|
||||
remote=remote_server)
|
||||
|
||||
print(profile)
|
||||
|
||||
|
||||
def _read_file(filename):
|
||||
"""Return the entire contens of a file as string."""
|
||||
with open(filename, 'r') as file_handle:
|
||||
return ''.join(file_handle.readlines())
|
||||
|
||||
|
||||
def main():
|
||||
"""Parse arguments and perform all duties."""
|
||||
arguments = parse_arguments()
|
||||
|
||||
subcommand = arguments.subcommand.replace('-', '_')
|
||||
subcommand_method = globals()['subcommand_' + subcommand]
|
||||
subcommand_method(arguments)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
1
data/etc/plinth/modules-enabled/openvpn
Normal file
1
data/etc/plinth/modules-enabled/openvpn
Normal file
@ -0,0 +1 @@
|
||||
plinth.modules.openvpn
|
||||
68
plinth/modules/openvpn/__init__.py
Normal file
68
plinth/modules/openvpn/__init__.py
Normal file
@ -0,0 +1,68 @@
|
||||
#
|
||||
# This file is part of Plinth.
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
"""
|
||||
Plinth module to configure OpenVPN server.
|
||||
"""
|
||||
|
||||
from gettext import gettext as _
|
||||
|
||||
from plinth import actions
|
||||
from plinth import action_utils
|
||||
from plinth import cfg
|
||||
from plinth import service as service_module
|
||||
|
||||
|
||||
depends = ['plinth.modules.apps']
|
||||
|
||||
service = None
|
||||
|
||||
|
||||
def init():
|
||||
"""Intialize the OpenVPN module."""
|
||||
menu = cfg.main_menu.get('apps:index')
|
||||
menu.add_urlname(_('Virtual Private Network (OpenVPN)'), 'glyphicon-lock',
|
||||
'openvpn:index', 850)
|
||||
|
||||
global service
|
||||
service = service_module.Service(
|
||||
'openvpn', _('OpenVPN'), ['openvpn'],
|
||||
is_external=True, enabled=is_enabled())
|
||||
|
||||
|
||||
def is_enabled():
|
||||
"""Return whether the module is enabled."""
|
||||
return action_utils.service_is_enabled('openvpn@freedombox')
|
||||
|
||||
|
||||
def is_running():
|
||||
"""Return whether the service is running."""
|
||||
return action_utils.service_is_running('openvpn@freedombox')
|
||||
|
||||
|
||||
def is_setup():
|
||||
"""Return whether the service is running."""
|
||||
return actions.superuser_run('openvpn', ['is-setup']).strip() == 'true'
|
||||
|
||||
|
||||
def diagnose():
|
||||
"""Run diagnostics and return the results."""
|
||||
results = []
|
||||
|
||||
results.append(action_utils.diagnose_port_listening(1194, 'udp4'))
|
||||
|
||||
return results
|
||||
30
plinth/modules/openvpn/forms.py
Normal file
30
plinth/modules/openvpn/forms.py
Normal file
@ -0,0 +1,30 @@
|
||||
#
|
||||
# This file is part of Plinth.
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
"""
|
||||
Plinth module for configuring OpenVPN.
|
||||
"""
|
||||
|
||||
from django import forms
|
||||
from gettext import gettext as _
|
||||
|
||||
|
||||
class OpenVpnForm(forms.Form): # pylint: disable=W0232
|
||||
"""OpenVPN configuration form."""
|
||||
enabled = forms.BooleanField(
|
||||
label=_('Enable OpenVPN server'),
|
||||
required=False)
|
||||
116
plinth/modules/openvpn/templates/openvpn.html
Normal file
116
plinth/modules/openvpn/templates/openvpn.html
Normal file
@ -0,0 +1,116 @@
|
||||
{% extends "base.html" %}
|
||||
{% comment %}
|
||||
#
|
||||
# This file is part of Plinth.
|
||||
#
|
||||
# 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 %}
|
||||
|
||||
|
||||
{% block page_head %}
|
||||
|
||||
{% if status.setup_running %}
|
||||
<meta http-equiv="refresh" content="3" />
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h2>Virtual Private Network (OpenVPN)</h2>
|
||||
|
||||
<p>Virtual Private Network (VPN) is a technique for securely
|
||||
connecting two machines in order to access resources of a private
|
||||
network. While you are away from home, you can connect to your
|
||||
{{ cfg.box_name }} in order to join your home network and access
|
||||
private/internal services provided by {{ cfg.box_name }}. You can
|
||||
also access the rest of the Internet via {{ cfg.box_name }} for
|
||||
added security and anonymity.</p>
|
||||
|
||||
{% if status.is_setup %}
|
||||
|
||||
<h3>Profile</h3>
|
||||
|
||||
<p>To connect to {{ cfg.box_name }}'s VPN, you need to download a
|
||||
profile and feed it to an OpenVPN client on your mobile or
|
||||
desktop machine. OpenVPN Clients are available for most
|
||||
platforms. See
|
||||
<a href="https://wiki.debian.org/FreedomBox/Manual/OpenVPN"
|
||||
title="FreedomBox Manual - OpenVPN">documentation</a> on
|
||||
recommended clients and instructions on how to configure them.</p>
|
||||
|
||||
<p>Profile is specific to each user of {{ cfg.box_name }}. Keep
|
||||
it a secret.</p>
|
||||
|
||||
<form class="form" method="post" action="{% url 'openvpn:profile' %}">
|
||||
{% csrf_token %}
|
||||
|
||||
<input type="submit" class="btn btn-primary" value="Download my profile"/>
|
||||
</form>
|
||||
|
||||
{% endif %}
|
||||
|
||||
<h3>Status</h3>
|
||||
|
||||
{% if not status.is_setup and not status.setup_running %}
|
||||
<p>OpenVPN has not yet been setup. Performing a secure setups
|
||||
takes a very long time. Depending on how fast your
|
||||
{{ cfg.box_name }} is, it may even take hours. If the setup is
|
||||
interrupted, you may start it again.</p>
|
||||
|
||||
<form class="form" method="post" action="{% url 'openvpn:setup' %}">
|
||||
{% csrf_token %}
|
||||
|
||||
<input type="submit" class="btn btn-primary" value="Start setup"/>
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
{% if not status.is_setup and status.setup_running %}
|
||||
<p class="running-status-parent">
|
||||
<span class='running-status active'></span> OpenVPN setup is running
|
||||
</p>
|
||||
|
||||
<p>To perform a secure setup, this process takes a very long time.
|
||||
Depending on how fast your {{ cfg.box_name }} is, it may even
|
||||
take hours. If the setup is interrupted, you may start it
|
||||
again.</p>
|
||||
{% endif %}
|
||||
|
||||
{% if status.is_setup %}
|
||||
<p class="running-status-parent">
|
||||
{% if status.is_running %}
|
||||
<span class='running-status active'></span> OpenVPN server is running
|
||||
{% else %}
|
||||
<span class='running-status inactive'></span> OpenVPN server is not running
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
{% include "diagnostics_button.html" with module="openvpn" %}
|
||||
|
||||
<h3>Configuration</h3>
|
||||
|
||||
<form class="form" method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
{{ form|bootstrap }}
|
||||
|
||||
<input type="submit" class="btn btn-primary" value="Update setup"/>
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
0
plinth/modules/openvpn/tests/__init__.py
Normal file
0
plinth/modules/openvpn/tests/__init__.py
Normal file
30
plinth/modules/openvpn/urls.py
Normal file
30
plinth/modules/openvpn/urls.py
Normal file
@ -0,0 +1,30 @@
|
||||
#
|
||||
# This file is part of Plinth.
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
"""
|
||||
URLs for the OpenVPN module.
|
||||
"""
|
||||
|
||||
from django.conf.urls import patterns, url
|
||||
|
||||
|
||||
urlpatterns = patterns( # pylint: disable-msg=C0103
|
||||
'plinth.modules.openvpn.views',
|
||||
url(r'^apps/openvpn/$', 'index', name='index'),
|
||||
url(r'^apps/openvpn/setup/$', 'setup', name='setup'),
|
||||
url(r'^apps/openvpn/profile/$', 'profile', name='profile'),
|
||||
)
|
||||
144
plinth/modules/openvpn/views.py
Normal file
144
plinth/modules/openvpn/views.py
Normal file
@ -0,0 +1,144 @@
|
||||
#
|
||||
# This file is part of Plinth.
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
"""
|
||||
Plinth module for configuring OpenVPN server.
|
||||
"""
|
||||
|
||||
from django.contrib import messages
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import redirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.views.decorators.http import require_POST
|
||||
from gettext import gettext as _
|
||||
import logging
|
||||
|
||||
from .forms import OpenVpnForm
|
||||
from plinth import actions
|
||||
from plinth import package
|
||||
from plinth.modules import openvpn
|
||||
from plinth.modules.config import config
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
setup_process = None
|
||||
|
||||
|
||||
@package.required(['openvpn', 'easy-rsa'])
|
||||
def index(request):
|
||||
"""Serve configuration page."""
|
||||
status = get_status()
|
||||
|
||||
if status['setup_running']:
|
||||
_collect_setup_result(request)
|
||||
|
||||
form = None
|
||||
|
||||
if request.method == 'POST':
|
||||
form = OpenVpnForm(request.POST, prefix='openvpn')
|
||||
# pylint: disable=E1101
|
||||
if form.is_valid():
|
||||
_apply_changes(request, status, form.cleaned_data)
|
||||
status = get_status()
|
||||
form = OpenVpnForm(initial=status, prefix='openvpn')
|
||||
else:
|
||||
form = OpenVpnForm(initial=status, prefix='openvpn')
|
||||
|
||||
return TemplateResponse(request, 'openvpn.html',
|
||||
{'title': _('Virtual Private Network (OpenVPN)'),
|
||||
'status': status,
|
||||
'form': form})
|
||||
|
||||
|
||||
@require_POST
|
||||
def setup(request):
|
||||
"""Start the setup process."""
|
||||
openvpn.service.notify_enabled(None, True)
|
||||
|
||||
global setup_process
|
||||
if not openvpn.is_setup() and not setup_process:
|
||||
setup_process = actions.superuser_run('openvpn', ['setup'], async=True)
|
||||
|
||||
return redirect('openvpn:index')
|
||||
|
||||
|
||||
@require_POST
|
||||
def profile(request):
|
||||
"""Provide the user's profile for download."""
|
||||
username = request.user.username
|
||||
domainname = config.get_domainname()
|
||||
|
||||
if not config.get_domainname():
|
||||
domainname = config.get_hostname()
|
||||
|
||||
profile_string = actions.superuser_run(
|
||||
'openvpn', ['get-profile', username, domainname])
|
||||
|
||||
response = HttpResponse(profile_string,
|
||||
content_type='application/x-openvpn-profile')
|
||||
response['Content-Disposition'] = \
|
||||
'attachment; filename={username}.ovpn'.format(username=username)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
def get_status():
|
||||
"""Get the current settings from Transmission server."""
|
||||
status = {'is_setup': openvpn.is_setup(),
|
||||
'setup_running': False,
|
||||
'enabled': openvpn.is_enabled(),
|
||||
'is_running': openvpn.is_running()}
|
||||
|
||||
status['setup_running'] = bool(setup_process)
|
||||
|
||||
return status
|
||||
|
||||
|
||||
def _collect_setup_result(request):
|
||||
"""Handle setup process is completion."""
|
||||
global setup_process
|
||||
if not setup_process:
|
||||
return
|
||||
|
||||
return_code = setup_process.poll()
|
||||
|
||||
# Setup process is not complete yet
|
||||
if return_code == None:
|
||||
return
|
||||
|
||||
if not return_code:
|
||||
messages.success(request, _('Setup completed.'))
|
||||
else:
|
||||
messages.info(request, _('Setup failed.'))
|
||||
|
||||
setup_process = None
|
||||
|
||||
|
||||
def _apply_changes(request, old_status, new_status):
|
||||
"""Apply the changes."""
|
||||
modified = False
|
||||
|
||||
if old_status['enabled'] != new_status['enabled']:
|
||||
sub_command = 'enable' if new_status['enabled'] else 'disable'
|
||||
actions.superuser_run('openvpn', [sub_command])
|
||||
openvpn.service.notify_enabled(None, new_status['enabled'])
|
||||
modified = True
|
||||
|
||||
if modified:
|
||||
messages.success(request, _('Configuration updated'))
|
||||
else:
|
||||
messages.info(request, _('Setting unchanged'))
|
||||
Loading…
x
Reference in New Issue
Block a user