Browse Source

Merge pull request #4 from jvaubourg/dev

New version of VPN Client (works with a YunoHost release using dnsmasq, not in stable for now)
Julien Vaubourg 10 years ago
parent
commit
39c98bbaea

+ 10 - 0
README.md

@@ -21,3 +21,13 @@ See the <a href="https://raw.githubusercontent.com/jvaubourg/hotspot_ynh/master/
 * Set an IPv6 from your delegated prefix (*prefix::42*) on the server, to use for the AAAA records
 * Use native IPv6 if available for creating the tunnel
 * Web interface ([screenshot](https://raw.githubusercontent.com/jvaubourg/vpnclient_ynh/master/screenshot.png))
+
+## Prerequisites
+
+This app works with a non-stable version of YunoHost.
+
+Until this version is available (coming soon!) as an official stable release, you need to execute some commands before installing this app:
+
+    # service bind9 stop
+    # update-rc.d bind9 remove
+    # apt-get install dnsmasq

+ 21 - 1
conf/init_ynh-vpnclient

@@ -205,6 +205,7 @@ if [ "$1" != restart ]; then
   
   echo -n "Retrieving Yunohost settings... "
   
+  ynh_service_enabled=$(moulinette_get service_enabled)
   ynh_server_name=$(moulinette_get server_name)
   ynh_server_port=$(moulinette_get server_port)
   ynh_server_proto=$(moulinette_get server_proto)
@@ -233,6 +234,8 @@ case "${1}" in
   start)
     if is_running; then
       echo "Already started"
+    elif [ "${ynh_service_enabled}" -eq 0 ]; then
+      echo "Disabled service"
     else
       echo "[vpnclient] Starting..."
       touch /tmp/.ynh-vpnclient-started
@@ -281,6 +284,10 @@ case "${1}" in
       moulinette_set ip6_gw "${new_ip6_gw}"
       moulinette_set wired_device "${new_wired_device}"
 
+      # Restart dhcpd
+      service bind9 stop &> /dev/null
+      service dnsmasq restart
+
       # Restart hotspot if needed
       if has_hotspot_app && ! is_hotspot_knowme; then
         service ynh-hotspot start
@@ -304,11 +311,19 @@ case "${1}" in
     if is_openvpn_running; then
       echo "Stop openvpn"
       stop_openvpn
+
+      i=0; true && while [ $? -eq 0 ]; do
+        sleep 1 && (( i++ ))
+        [ ${i} -gt 20 ] && exit 1
+        ip link show dev tun0 &> /dev/null
+      done
     fi
 
-    if has_hotspot_app && ! is_hotspot_knowme; then
+    if has_hotspot_app && is_hotspot_knowme; then
       service ynh-hotspot start
     fi
+
+    service dnsmasq restart
   ;;
   restart)
     $0 stop
@@ -317,6 +332,11 @@ case "${1}" in
   status)
     exitcode=0
 
+    if [ "${ynh_service_enabled}" -eq 0 ]; then
+      echo "[ERR] VPN Client Service disabled"
+      exitcode=1
+    fi
+
     echo "[INFO] Autodetected internet interface: ${new_wired_device} (last start: ${old_wired_device})"
     echo "[INFO] Autodetected IPv6 address for the VPN server: ${new_server_ip6} (last start: ${old_server_ip6})"
 

+ 2 - 0
conf/phpfpm_vpnadmin.conf

@@ -196,5 +196,7 @@ catch_workers_output = no
 ;
 ; Note: path INI options can be relative and will be expanded with the prefix
 ; (pool, global or /usr)
+
+php_value[max_execution_time] = 600
 php_value[upload_max_filesize] = 10G
 php_value[post_max_size] = 10G

BIN
screenshot.png


+ 4 - 1
scripts/install

@@ -81,6 +81,8 @@ fi
 
 # Install packages
 packages='php5-fpm sipcalc openvpn'
+export DEBIAN_FRONTEND=noninteractive
+
 sudo apt-get --assume-yes --force-yes install ${packages}
 
 if [ $? -ne 0 ]; then
@@ -89,6 +91,7 @@ if [ $? -ne 0 ]; then
 fi
 
 # Save arguments
+sudo yunohost app setting vpnclient service_enabled -v 1
 sudo yunohost app setting vpnclient server_name -v "${server_name}"
 sudo yunohost app setting vpnclient server_port -v 1194
 sudo yunohost app setting vpnclient server_proto -v udp
@@ -102,6 +105,7 @@ sudo install -o root -g root -m 0755 ../conf/ipv6_expanded /usr/local/bin/
 sudo install -o root -g root -m 0755 ../conf/ipv6_compressed /usr/local/bin/
 
 # Copy confs
+sudo mkdir -pm 0755 /var/log/nginx/
 sudo chown root:admins /etc/openvpn/
 sudo chmod 775 /etc/openvpn/
 
@@ -153,7 +157,6 @@ sudo sed 's|<TPL:PHP_NAME>|vpnadmin|g' -i /etc/php5/fpm/pool.d/vpnadmin.conf
 sudo sed 's|<TPL:PHP_USER>|admin|g' -i /etc/php5/fpm/pool.d/vpnadmin.conf
 sudo sed 's|<TPL:PHP_GROUP>|admins|g' -i /etc/php5/fpm/pool.d/vpnadmin.conf
 sudo sed 's|<TPL:NGINX_REALPATH>|/var/www/vpnadmin/|g' -i /etc/php5/fpm/pool.d/vpnadmin.conf
-sudo sed 's|^;\?\s*max_execution_time.\+|max_execution_time = 600|' -i /etc/php5/fpm/php.ini
 
 # Fix sources
 sudo sed "s|<TPL:NGINX_LOCATION>|${url_path}|g" -i /var/www/vpnadmin/config.php

+ 90 - 79
sources/controller.php

@@ -65,6 +65,7 @@ dispatch('/', function() {
   $ip6_net = ($ip6_net == 'none') ? '' : $ip6_net;
   $raw_openvpn = file_get_contents('/etc/openvpn/client.conf.tpl');
 
+  set('service_enabled', moulinette_get('service_enabled'));
   set('server_name', moulinette_get('server_name'));
   set('server_port', moulinette_get('server_port'));
   set('server_proto', moulinette_get('server_proto'));
@@ -85,99 +86,109 @@ dispatch_put('/settings', function() {
   $crt_client_key_exists = file_exists('/etc/openvpn/keys/user.key');
   $crt_server_ca_exists = file_exists('/etc/openvpn/keys/ca-server.crt');
 
+  $service_enabled = isset($_POST['service_enabled']) ? 1 : 0;
   $ip6_net = empty($_POST['ip6_net']) ? 'none' : $_POST['ip6_net'];
   $ip6_addr = 'none';
 
-  try {
-    if(empty($_POST['server_name']) || empty($_POST['server_port']) || empty($_POST['server_proto'])) {
-      throw new Exception(T_('The Server Address, the Server Port and the Protocol cannot be empty'));
-    }
-  
-    if(!preg_match('/^\d+$/', $_POST['server_port'])) {
-      throw new Exception(T_('The Server Port must be only composed of digits'));
-    }
-  
-    if($_POST['server_proto'] != 'udp' && $_POST['server_proto'] != 'tcp') {
-      throw new Exception(T_('The Protocol must be "udp" or "tcp"'));
+  if($service_enabled == 1) {
+    try {
+      if(empty($_POST['server_name']) || empty($_POST['server_port']) || empty($_POST['server_proto'])) {
+        throw new Exception(T_('The Server Address, the Server Port and the Protocol cannot be empty'));
+      }
+    
+      if(!preg_match('/^\d+$/', $_POST['server_port'])) {
+        throw new Exception(T_('The Server Port must be only composed of digits'));
+      }
+    
+      if($_POST['server_proto'] != 'udp' && $_POST['server_proto'] != 'tcp') {
+        throw new Exception(T_('The Protocol must be "udp" or "tcp"'));
+      }
+    
+      if(($_FILES['crt_client']['error'] == UPLOAD_ERR_OK && $_FILES['crt_client_key']['error'] != UPLOAD_ERR_OK && (!$crt_client_key_exists || $_POST['crt_client_key_delete'] == 1))
+        || ($_FILES['crt_client_key']['error'] == UPLOAD_ERR_OK && $_FILES['crt_client']['error'] != UPLOAD_ERR_OK && (!$crt_client_exists || $_POST['crt_client_delete'] == 1))) {
+    
+        throw new Exception(T_('A Client Certificate is needed when you suggest a Key, or vice versa'));
+      }
+    
+      if(empty($_POST['login_user']) xor empty($_POST['login_passphrase'])) {
+        throw new Exception(T_('A Password is needed when you suggest a Username, or vice versa'));
+      }
+    
+      if($_FILES['crt_server_ca']['error'] != UPLOAD_ERR_OK && !$crt_server_ca_exists) {
+        throw new Exception(T_('You need a Server CA.'));
+      }
+    
+      if(($_FILES['crt_client_key']['error'] != UPLOAD_ERR_OK && (!$crt_client_key_exists || $_POST['crt_client_key_delete'] == 1)) && empty($_POST['login_user'])) {
+        throw new Exception(T_('You need either a Client Certificate, either a Username, or both'));
+      }
+    
+      if($ip6_net != 'none') {
+        $ip6_net = ipv6_expanded($ip6_net);
+    
+        if(empty($ip6_net)) {
+          throw new Exception(T_('The IPv6 Delegated Prefix format looks bad'));
+        }
+    
+        $ip6_blocs = explode(':', $ip6_net);
+        $ip6_addr = "${ip6_blocs[0]}:${ip6_blocs[1]}:${ip6_blocs[2]}:${ip6_blocs[3]}:${ip6_blocs[4]}:${ip6_blocs[5]}:${ip6_blocs[6]}:42";
+    
+        $ip6_net = ipv6_compressed($ip6_net);
+        $ip6_addr = ipv6_compressed($ip6_addr);
+      }
+
+    } catch(Exception $e) {
+      flash('error', $e->getMessage().' ('.T_('configuration not updated').').');
+      goto redirect;
     }
+  }
   
-    if(($_FILES['crt_client']['error'] == UPLOAD_ERR_OK && $_FILES['crt_client_key']['error'] != UPLOAD_ERR_OK && (!$crt_client_key_exists || $_POST['crt_client_key_delete'] == 1))
-      || ($_FILES['crt_client_key']['error'] == UPLOAD_ERR_OK && $_FILES['crt_client']['error'] != UPLOAD_ERR_OK && (!$crt_client_exists || $_POST['crt_client_delete'] == 1))) {
+  stop_service();
   
-      throw new Exception(T_('A Client Certificate is needed when you suggest a Key, or vice versa'));
+  moulinette_set('service_enabled', $service_enabled);
+
+  if($service_enabled == 1) {
+    moulinette_set('server_name', $_POST['server_name']);
+    moulinette_set('server_port', $_POST['server_port']);
+    moulinette_set('server_proto', $_POST['server_proto']);
+    moulinette_set('login_user', $_POST['login_user']);
+    moulinette_set('login_passphrase', $_POST['login_passphrase']);
+    moulinette_set('ip6_net', $ip6_net);
+    moulinette_set('ip6_addr', $ip6_addr);
+    
+    file_put_contents('/etc/openvpn/client.conf.tpl', $_POST['raw_openvpn']);
+
+    if($_FILES['crt_client']['error'] == UPLOAD_ERR_OK) {
+      move_uploaded_file($_FILES['crt_client']['tmp_name'], '/etc/openvpn/keys/user.crt');
+    } elseif($_POST['crt_client_delete'] == 1) {
+      unlink('/etc/openvpn/keys/user.crt');
     }
-  
-    if(empty($_POST['login_user']) xor empty($_POST['login_passphrase'])) {
-      throw new Exception(T_('A Password is needed when you suggest a Username, or vice versa'));
+    
+    if($_FILES['crt_client_key']['error'] == UPLOAD_ERR_OK) {
+      move_uploaded_file($_FILES['crt_client_key']['tmp_name'], '/etc/openvpn/keys/user.key');
+    } elseif($_POST['crt_client_key_delete'] == 1) {
+      unlink('/etc/openvpn/keys/user.key');
     }
-  
-    if($_FILES['crt_server_ca']['error'] != UPLOAD_ERR_OK && !$crt_server_ca_exists) {
-      throw new Exception(T_('You need a Server CA.'));
+    
+    if($_FILES['crt_server_ca']['error'] == UPLOAD_ERR_OK) {
+      move_uploaded_file($_FILES['crt_server_ca']['tmp_name'], '/etc/openvpn/keys/ca-server.crt');
     }
-  
-    if(($_FILES['crt_client_key']['error'] != UPLOAD_ERR_OK && (!$crt_client_key_exists || $_POST['crt_client_key_delete'] == 1)) && empty($_POST['login_user'])) {
-      throw new Exception(T_('You need either a Client Certificate, either a Username, or both'));
-    }
-  
-    if($ip6_net != 'none') {
-      $ip6_net = ipv6_expanded($ip6_net);
-  
-      if(empty($ip6_net)) {
-        throw new Exception(T_('The IPv6 Delegated Prefix format looks bad'));
-      }
-  
-      $ip6_blocs = explode(':', $ip6_net);
-      $ip6_addr = "${ip6_blocs[0]}:${ip6_blocs[1]}:${ip6_blocs[2]}:${ip6_blocs[3]}:${ip6_blocs[4]}:${ip6_blocs[5]}:${ip6_blocs[6]}:42";
-  
-      $ip6_net = ipv6_compressed($ip6_net);
-      $ip6_addr = ipv6_compressed($ip6_addr);
+    
+    if(!empty($_POST['login_user'])) {
+      file_put_contents('/etc/openvpn/keys/credentials', "${_POST['login_user']}\n${_POST['login_passphrase']}");
+    } else {
+      file_put_contents('/etc/openvpn/keys/credentials', '');
     }
 
-  } catch(Exception $e) {
-    flash('error', $e->getMessage().' ('.T_('configuration not updated').').');
-    goto redirect;
-  }
-  
-  stop_service();
-  
-  moulinette_set('server_name', $_POST['server_name']);
-  moulinette_set('server_port', $_POST['server_port']);
-  moulinette_set('server_proto', $_POST['server_proto']);
-  moulinette_set('login_user', $_POST['login_user']);
-  moulinette_set('login_passphrase', $_POST['login_passphrase']);
-  moulinette_set('ip6_net', $ip6_net);
-  moulinette_set('ip6_addr', $ip6_addr);
-  
-  file_put_contents('/etc/openvpn/client.conf.tpl', $_POST['raw_openvpn']);
+    $retcode = start_service();
 
-  if($_FILES['crt_client']['error'] == UPLOAD_ERR_OK) {
-    move_uploaded_file($_FILES['crt_client']['tmp_name'], '/etc/openvpn/keys/user.crt');
-  } elseif($_POST['crt_client_delete'] == 1) {
-    unlink('/etc/openvpn/keys/user.crt');
-  }
-  
-  if($_FILES['crt_client_key']['error'] == UPLOAD_ERR_OK) {
-    move_uploaded_file($_FILES['crt_client_key']['tmp_name'], '/etc/openvpn/keys/user.key');
-  } elseif($_POST['crt_client_key_delete'] == 1) {
-    unlink('/etc/openvpn/keys/user.key');
-  }
-  
-  if($_FILES['crt_server_ca']['error'] == UPLOAD_ERR_OK) {
-    move_uploaded_file($_FILES['crt_server_ca']['tmp_name'], '/etc/openvpn/keys/ca-server.crt');
-  }
-  
-  if(!empty($_POST['login_user'])) {
-    file_put_contents('/etc/openvpn/keys/credentials', "${_POST['login_user']}\n${_POST['login_passphrase']}");
-  } else {
-    file_put_contents('/etc/openvpn/keys/credentials', '');
-  }
-
-  $retcode = start_service();
+    if($retcode == 0) {
+      flash('success', T_('Configuration updated and service successfully reloaded'));
+    } else {
+      flash('error', T_('Configuration updated but service reload failed'));
+    }
 
-  if($retcode == 0) {
-    flash('success', T_('Configuration updated and service successfully reloaded'));
   } else {
-    flash('error', T_('Configuration updated but service reload failed'));
+      flash('success', T_('Service successfully disabled'));
   }
 
   redirect:

+ 28 - 0
sources/public/css/bootstrap-toggle.min.css

@@ -0,0 +1,28 @@
+/*! ========================================================================
+ * Bootstrap Toggle: bootstrap-toggle.css v2.0.0
+ * http://www.bootstraptoggle.com
+ * ========================================================================
+ * Copyright 2014 Min Hur, The New York Times Company
+ * Licensed under MIT
+ * ======================================================================== */
+.checkbox label .toggle,.checkbox-inline .toggle{margin-left:-20px;margin-right:5px}
+.toggle{position:relative;overflow:hidden}
+.toggle input[type=checkbox]{display:none}
+.toggle-group{position:absolute;width:200%;top:0;bottom:0;left:0;transition:left .35s;-webkit-transition:left .35s;-moz-user-select:none;-webkit-user-select:none}
+.toggle.off .toggle-group{left:-100%}
+.toggle-on{position:absolute;top:0;bottom:0;left:0;right:50%;margin:0;border:0;border-radius:0}
+.toggle-off{position:absolute;top:0;bottom:0;left:50%;right:0;margin:0;border:0;border-radius:0}
+.toggle-handle{position:relative;margin:0 auto;padding-top:0;padding-bottom:0;height:100%;width:0;border-width:0 1px}
+.toggle.btn{min-width:59px;min-height:34px}
+.toggle-on.btn{padding-right:24px}
+.toggle-off.btn{padding-left:24px}
+.toggle.btn-lg{min-width:79px;min-height:45px}
+.toggle-on.btn-lg{padding-right:31px}
+.toggle-off.btn-lg{padding-left:31px}
+.toggle-handle.btn-lg{width:40px}
+.toggle.btn-sm{min-width:50px;min-height:30px}
+.toggle-on.btn-sm{padding-right:20px}
+.toggle-off.btn-sm{padding-left:20px}
+.toggle.btn-xs{min-width:35px;min-height:22px}
+.toggle-on.btn-xs{padding-right:12px}
+.toggle-off.btn-xs{padding-left:12px}

File diff suppressed because it is too large
+ 9 - 0
sources/public/js/bootstrap-toggle.min.js


+ 10 - 0
sources/public/js/custom.js

@@ -20,6 +20,8 @@ $(document).ready(function() {
   $('.btn-group').button();
   $('[data-toggle="tooltip"]').tooltip();
 
+  $('.switch').bootstrapToggle();
+
   $('.fileinput').click(function() {
     if(!$(this).hasClass('btn-danger')) {
       var realinputid = '#' + $(this).attr('id').replace(/_chooser.*/, '');
@@ -87,4 +89,12 @@ $(document).ready(function() {
     $('#raw_openvpn_btnpanel').hide();
     $('#raw_openvpn_panel').show('low');
   });
+
+  $('#service_enabled').change(function() {
+    if($('#service_enabled').parent().hasClass('off')) {
+      $('.enabled').hide('slow');
+    } else {
+      $('.enabled').show('slow');
+    }
+  });
 });

+ 2 - 0
sources/views/layout.html.php

@@ -32,10 +32,12 @@
 
   <link media="all" type="text/css" href="<?= PUBLIC_DIR ?>/bootstrap/css/bootstrap.min.css" rel="stylesheet">
   <link media="all" type="text/css" href="<?= PUBLIC_DIR ?>/bootstrap/css/bootstrap-theme.min.css" rel="stylesheet">
+  <link media="all" type="text/css" href="<?= PUBLIC_DIR ?>/css/bootstrap-toggle.min.css" rel="stylesheet">
   <link media="all" type="text/css" href="<?= PUBLIC_DIR ?>/css/style.css" rel="stylesheet">
 
   <script src="<?= PUBLIC_DIR ?>/jquery/jquery-2.1.1.min.js"></script>
   <script src="<?= PUBLIC_DIR ?>/bootstrap/js/bootstrap.min.js"></script>
+  <script src="<?= PUBLIC_DIR ?>/js/bootstrap-toggle.min.js"></script>
   <script src="<?= PUBLIC_DIR ?>/js/custom.js"></script>
 </head>
 

+ 21 - 4
sources/views/settings.html.php

@@ -40,6 +40,23 @@
 
       <div class="panel panel-default">
         <div class="panel-heading">
+          <h3 class="panel-title"><?= T_("Service") ?></h3>
+        </div>
+
+        <div style="padding: 14px 14px 0 10px">
+          <div class="form-group">
+            <label for="wifi_secure" class="col-sm-3 control-label"><?= T_('VPN Enabled') ?></label>
+            <div class="col-sm-9 input-group-btn">
+              <div class="input-group">
+                <input type="checkbox" class="form-control switch" name="service_enabled" id="service_enabled" value="1" <?= $service_enabled == 1 ? 'checked="checked"' : '' ?> />
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div class="panel panel-default enabled" <?= $service_enabled == 0 ? 'style="display: none"' : '' ?>>
+        <div class="panel-heading">
           <h3 class="panel-title"><?= T_("VPN") ?></h3>
         </div>
 
@@ -87,7 +104,7 @@
         </div>
       </div>
 
-      <div class="panel panel-default">
+      <div class="panel panel-default enabled" <?= $service_enabled == 0 ? 'style="display: none"' : '' ?>>
         <div class="panel-heading">
           <h3 class="panel-title"><?= T_("IPv6") ?></h3>
         </div>
@@ -103,13 +120,13 @@
       </div>
 
       <?php if(!$crt_client_key_exists && empty($login_user)): ?>
-        <div class="alert alert-dismissible alert-warning fade in" style="margin: 2px 0px 17px" role="alert">
+        <div class="alert alert-dismissible alert-warning fade in enabled" <?= $service_enabled == 0 ? 'style="display: none"' : '' ?> style="margin: 2px 0px 17px" role="alert">
           <button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
           <strong><?= T_('Notice') ?>:</strong> <?= T_("You need to upload a Client Certificate, or define a Username (or both) for starting your VPN Client.") ?>
         </div>
       <?php endif; ?>
 
-      <div class="panel panel-default">
+      <div class="panel panel-default enabled" <?= $service_enabled == 0 ? 'style="display: none"' : '' ?>>
         <div class="panel-heading">
           <h3 class="panel-title"><?= T_("Certificates") ?></h3>
         </div>
@@ -163,7 +180,7 @@
         </div>
       </div>
 
-      <div class="panel panel-default">
+      <div class="panel panel-default enabled" <?= $service_enabled == 0 ? 'style="display: none"' : '' ?>>
         <div class="panel-heading">
           <h3 class="panel-title"><?= T_("Login") ?></h3>
         </div>