Browse Source

* Add input checks
* Add connections without certificate
* Fix bug with credentials update

Julien VAUBOURG 10 years ago
parent
commit
aac96974c6

+ 2 - 4
TODO

@@ -1,5 +1,3 @@
-* Check input parameters
-** in bash install script (empty parameters, are certificates exist, are certificate contain BEGIN CERTIFICATE/PRIVATE)
-** in PHP controller (empty parameters, parameters format, compressed IPv6, are certificate contain BEGIN CERTIFICATE/PRIVATE)
 * Translate PHP interface in French
-* Support VPN without certificates (only login)
+* Support VPN without certificates (only login) -- added need just tests
+** Add require for slapd for the service start -- added need just tests

+ 33 - 2
conf/init_ynh-vpnclient

@@ -1,7 +1,7 @@
 #!/bin/bash
 ### BEGIN INIT INFO
 # Provides: ynh-vpnclient
-# Required-Start:    $network $remote_fs $syslog
+# Required-Start:    $network $remote_fs $syslog slapd
 # Required-Stop:     $network $remote_fs $syslog
 # Default-Start:     2 3 4 5
 # Default-Stop:      0 1 6
@@ -80,6 +80,12 @@ start_openvpn() {
   sed "s|<TPL:SERVER_PORT>|${ynh_server_port}|g" -i /etc/openvpn/client.conf
   sed "s|<TPL:PROTO>|${proto}|g" -i /etc/openvpn/client.conf
 
+  if [ -e /etc/openvpn/keys/user.key ]; then
+    sed 's|^<TPL:CERT_COMMENT>||' -i /etc/openvpn/client.conf
+  else
+    sed 's|^<TPL:CERT_COMMENT>|;|' -i /etc/openvpn/client.conf
+  fi
+
   if [[ "${proto}" =~ udp ]]; then
     sed 's|^<TPL:UDP_COMMENT>||' -i /etc/openvpn/client.conf
   else
@@ -140,6 +146,21 @@ moulinette_set() {
   fi
 }
 
+# Check configuration consistency
+
+if [[ ! "${1}" =~ stop ]]; then
+  if [ ! -e /etc/openvpn/keys/ca-server.crt ]; then
+    echo "DISABLED SERVICE: You need a CA server (you can add it through the web admin)" >&2
+    exit 1
+  fi
+
+  find /etc/openvpn/keys/ -empty -name credentials &> /dev/null
+  if [ $? -eq 0 -a ! -e /etc/openvpn/keys/user.key ]; then
+    echo "DISABLED SERVICE: You need either a client certificate, either a username, or both (you can add one through the web admin)" >&2
+    exit 1
+  fi
+fi
+
 # Variables
 
 echo -n "Retrieving Yunohost settings... "
@@ -217,6 +238,9 @@ case "${1}" in
     moulinette_set ip6_gw "${new_ip6_gw}"
     moulinette_set wired_device "${new_wired_device}"
   ;;
+  litestop)
+    litestop=1
+  ;&
   stop)
     echo "Stopping..."
 
@@ -234,6 +258,13 @@ case "${1}" in
       echo "Stop openvpn"
       stop_openvpn
     fi
+
+    if [ -z "${litestop}" ]; then
+      yunohost app list -f hotspot --json | grep -q '"installed": true'
+      if [ $? -eq 0 ]; then
+        service ynh-hotspot start
+      fi
+    fi
   ;;
   status)
     exitcode=0
@@ -272,7 +303,7 @@ case "${1}" in
     exit ${exitcode}
   ;;
   *)
-    echo "Usage: $0 {start|stop|status}"
+    echo "Usage: $0 {start|stop|litestop|status}"
     exit 1
   ;;
 esac

+ 3 - 0
conf/ipv6_compressed

@@ -0,0 +1,3 @@
+#!/bin/bash
+
+sipcalc "${1}" | grep Compressed | awk '{ print $NF; }'

+ 3 - 0
conf/ipv6_expanded

@@ -0,0 +1,3 @@
+#!/bin/bash
+
+sipcalc "${1}" | grep Expanded | awk '{ print $NF; }'

+ 2 - 2
conf/openvpn_client.conf.tpl

@@ -20,8 +20,8 @@ port <TPL:SERVER_PORT>
 # TLS
 tls-client
 remote-cert-tls server
-cert /etc/openvpn/keys/user.crt
-key /etc/openvpn/keys/user.key
+<TPL:CERT_COMMENT>cert /etc/openvpn/keys/user.crt
+<TPL:CERT_COMMENT>key /etc/openvpn/keys/user.key
 ca /etc/openvpn/keys/ca-server.crt
 
 # Logs

+ 9 - 12
manifest.json

@@ -43,29 +43,26 @@
       {
         "name": "crt_client",
         "ask": {
-            "en": "Select the local path of your client certificate (will be moved)",
-            "fr": "Sélectionnez le chemin local de votre certificat client (le fichier sera déplacé)"
+            "en": "Select the local path of your client certificate (will be moved ; leave empty if not necessary or if you want to upload it later through the web admin)",
+            "fr": "Sélectionnez le chemin local de votre certificat client (le fichier sera déplacé ; laisser vide si non-nécessaire ou que vous souhaitez le téléverser plus tard via l'admin web)"
         },
-        "example": "/tmp/user.crt",
-        "default": "/tmp/user.crt"
+        "example": "/tmp/user.crt"
       },
       {
         "name": "crt_client_key",
         "ask": {
-            "en": "Select the local path of your client certificate key (will be moved)",
-            "fr": "Sélectionnez le chemin local de la clé de votre certificat client (le fichier sera déplacé)"
+            "en": "Select the local path of your client certificate key (will be moved ; leave empty if not necessary or if you want to upload it later through the web admin)",
+            "fr": "Sélectionnez le chemin local de la clé de votre certificat client (le fichier sera déplacé ; laisser vide si non-nécessaire ou que vous souhaitez le téléverser plus tard via l'admin web)"
         },
-        "example": "/tmp/user.key",
-        "default": "/tmp/user.key"
+        "example": "/tmp/user.key"
       },
       {
         "name": "crt_server_ca",
         "ask": {
-            "en": "Select the local path of the server CA (will be moved)",
-            "fr": "Sélectionnez le chemin local du CA du serveur (le fichier sera déplacé)"
+            "en": "Select the local path of the server CA (will be moved ; leave empty for uploading it later through the web admin)",
+            "fr": "Sélectionnez le chemin local du CA du serveur (le fichier sera déplacé ; laisser vide pour le téléverser plus tard via l'admin web)"
         },
-        "example": "/tmp/ca-server.crt",
-        "default": "/tmp/ca-server.crt"
+        "example": "/tmp/ca-server.crt"
       },
       {
         "name": "credentials_user",

BIN
screenshot.png


+ 65 - 6
scripts/install

@@ -11,7 +11,44 @@ login_user=${7}
 login_passphrase=${8}
 
 # Check arguments
-# TODO
+if [ -z "${server_name}" ]; then
+  echo "ERROR: You need a VPN server name" >&2
+  exit 1
+fi
+
+if [ \( -z "${crt_client_path}" -a ! -z "${crt_client_key_path}" \)\
+    -o \( ! -z "${crt_client_path}" -a -z "${crt_client_key_path}" \) ]; then
+
+  echo "ERROR: A client certificate is needed when you suggest a key (or vice versa)" >&2
+  exit 1
+fi
+
+if [ ! -z "${crt_client_key_path}" -a -z "${crt_server_ca_path}" ]; then
+  echo "ERROR: If you can suggest a local path for the client certificates, you probably can suggest one other for the (mandatory) CA server" >&2
+  exit 1
+fi
+
+if [ \( -z "${login_user}" -a ! -z "${login_passphrase}" \)\
+    -o \( ! -z "${login_user}" -a -z "${login_passphrase}" \) ]; then
+
+  echo "ERROR: A login password is needed when you suggest a login user (or vice versa)" >&2
+  exit 1
+fi
+
+if [ ! -z "${crt_client_path}" -a ! -f "${crt_client_path}" ]; then
+  echo "ERROR: The local path <${crt_client_path}> does not exist" >&2
+  exit 1
+fi
+
+if [ ! -z "${crt_client_key_path}" -a ! -f "${crt_client_key_path}" ]; then
+  echo "ERROR: The local path <${crt_client_key_path}> does not exist" >&2
+  exit 1
+fi
+
+if [ ! -z "${crt_server_ca_path}" -a ! -f "${crt_server_ca_path}" ]; then
+  echo "ERROR: The local path <${crt_server_ca_path}> does not exist" >&2
+  exit 1
+fi
 
 # Check domain/path availability
 sudo yunohost app checkurl ${domain}${url_path} -a vpnclient
@@ -31,6 +68,10 @@ sudo yunohost app setting vpnclient ip6_net -v none
 sudo yunohost app setting vpnclient login_user -v "${login_user}"
 sudo yunohost app setting vpnclient login_passphrase -v "${login_passphrase}"
 
+# Install IPv6 scripts
+sudo install -b -o root -g root -m 0755 ../conf/ipv6_expanded /usr/local/bin/
+sudo install -b -o root -g root -m 0755 ../conf/ipv6_compressed /usr/local/bin/
+
 # Copy confs
 sudo install -b -o root -g root -m 0644 ../conf/openvpn_client.conf.tpl /etc/openvpn/client.conf.tpl
 sudo install -b -o root -g root -m 0644 ../conf/nginx_vpnadmin.conf "/etc/nginx/conf.d/${domain}.d/vpnadmin.conf"
@@ -48,9 +89,14 @@ sudo find /var/www/vpnadmin/ -type d -exec chmod +x {} \;
 sudo mkdir -pm 0770 /etc/openvpn/keys/
 sudo chown root:admins /etc/openvpn/keys/
 
-sudo install -b -o root -g admins -m 0660 "${crt_client_path}" /etc/openvpn/keys/user.crt
-sudo install -b -o root -g admins -m 0660 "${crt_client_key_path}" /etc/openvpn/keys/user.key
-sudo install -b -o root -g admins -m 0660 "${crt_server_ca_path}" /etc/openvpn/keys/ca-server.crt
+[ ! -z "${crt_client_path}" ] &&\
+  sudo install -b -o root -g admins -m 0660 "${crt_client_path}" /etc/openvpn/keys/user.crt
+
+[ ! -z "${crt_client_key_path}" ] &&\
+  sudo install -b -o root -g admins -m 0660 "${crt_client_key_path}" /etc/openvpn/keys/user.key
+
+[ ! -z "${crt_server_ca_path}" ] &&\
+  sudo install -b -o root -g admins -m 0660 "${crt_server_ca_path}" /etc/openvpn/keys/ca-server.crt
 
 sudo rm -f "${crt_client_path}" "${crt_client_key_path}" "${crt_server_ca_path}"
 
@@ -98,7 +144,10 @@ sudo yunohost service start php5-fpm
 
 sudo yunohost service add ynh-vpnclient
 sudo yunohost service enable ynh-vpnclient
-sudo service ynh-vpnclient start
+
+if [ ! -z "${crt_server_ca_path}" ]; then
+  sudo service ynh-vpnclient start
+fi
 
 sudo service nginx reload
 
@@ -108,8 +157,18 @@ sudo yunohost app ssowatconf
 # Restart hotspot service if installed to change NAT configuration (now on tun0)
 # A new start will fix the interface without unsetting all stuff
 sudo yunohost app list -f hotspot --json | grep -q '"installed": true'
-if [ $? -eq 0 ]; then
+if [ $? -eq 0 -a ! -z "${crt_server_ca_path}" ]; then
   sudo service ynh-hotspot start
 fi
 
+# Check configuration consistency
+
+if [ -z "${crt_server_ca_path}" ]; then
+  echo "WARNING: VPN Client is not started because you need to define a server CA through the web admin" >&2
+fi
+
+if [ -z "${crt_client_key_path}" -a -z "${login_user}" ]; then
+  echo "WARNING: VPN Client is not started because you need either a client certificate, either a username (or both)" >&2
+fi
+
 exit 0

+ 0 - 7
scripts/remove

@@ -27,13 +27,6 @@ sudo rm -rf /var/www/vpnadmin/
 # Remove user
 sudo userdel -f vpnadmin
 
-# Restart hotspot service if installed to change NAT configuration
-# A new start will fix the interface without unsetting all stuff
-sudo yunohost app list -f hotspot --json | grep -q '"installed": true'
-if [ "$?" -eq 0 ]; then
-  sudo service ynh-hotspot start
-fi
-
 # Remove packets
 # The yunohost policy is currently to not uninstall packets (dependency problems)
 ## sudo apt-get --assume-yes --force-yes remove openvpn php5-fpm

+ 87 - 11
sources/controller.php

@@ -1,15 +1,15 @@
 <?php
 
 function moulinette_get($var) {
-  return htmlspecialchars(exec("sudo yunohost app setting vpnclient ".escapeshellarg($var)));
+  return htmlspecialchars(exec('sudo yunohost app setting vpnclient '.escapeshellarg($var)));
 }
 
 function moulinette_set($var, $value) {
-  return exec("sudo yunohost app setting vpnclient ".escapeshellarg($var)." -v ".escapeshellarg($value));
+  return exec('sudo yunohost app setting vpnclient '.escapeshellarg($var).' -v '.escapeshellarg($value));
 }
 
 function stop_service() {
-  exec('sudo service ynh-vpnclient stop');
+  exec('sudo service ynh-vpnclient litestop');
 }
 
 function start_service() {
@@ -18,6 +18,18 @@ function start_service() {
   return $retcode;
 }
 
+function ipv6_expanded($ip) {
+  exec('ipv6_expanded '.escapeshellarg($ip), $output);
+
+  return $output[0];
+}
+
+function ipv6_compressed($ip) {
+  exec('ipv6_compressed '.escapeshellarg($ip), $output);
+
+  return $output[0];
+}
+
 dispatch('/', function() {
   $ip6_net = moulinette_get('ip6_net');
   $ip6_net = ($ip6_net == 'none') ? '' : $ip6_net;
@@ -28,12 +40,72 @@ dispatch('/', function() {
   set('login_user', moulinette_get('login_user'));
   set('login_passphrase', moulinette_get('login_passphrase'));
   set('ip6_net', $ip6_net);
+  set('crt_client_exists', file_exists('/etc/openvpn/keys/user.crt'));
+  set('crt_client_key_exists', file_exists('/etc/openvpn/keys/user.key'));
+  set('crt_server_ca_exists', file_exists('/etc/openvpn/keys/ca-server.crt'));
 
   return render('settings.html.php');
 });
 
 dispatch_put('/settings', function() {
+  $crt_client_exists = file_exists('/etc/openvpn/keys/user.crt');
+  $crt_client_key_exists = file_exists('/etc/openvpn/keys/user.key');
+  $crt_server_ca_exists = file_exists('/etc/openvpn/keys/ca-server.crt');
+
   $ip6_net = empty($_POST['ip6_net']) ? 'none' : $_POST['ip6_net'];
+  $ip6_addr = 'none';
+
+  if(empty($_POST['server_name']) || empty($_POST['server_port']) || empty($_POST['server_proto'])) {
+    flash('error', T_('The Server Address, the Server Port and the Protocol cannot be empty.'));
+    goto redirect;
+  }
+
+  if(!preg_match('/^\d+$/', $_POST['server_port'])) {
+    flash('error', T_('The Server Port must be only composed of digits.'));
+    goto redirect;
+  }
+
+  if($_POST['server_proto'] != 'udp' && $_POST['server_proto'] != 'tcp') {
+    flash('error', T_('The Protocol must be "udp" or "tcp".'));
+    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))) {
+
+    flash('error', T_('A Client Certificate is needed when you suggest a Key (or vice versa).'));
+    goto redirect;
+  }
+
+  if(empty($_POST['login_user']) xor empty($_POST['login_passphrase'])) {
+    flash('error', T_('A Password is needed when you suggest a Username (or vice versa).'));
+    goto redirect;
+  }
+
+  if($_FILES['crt_server_ca']['error'] != UPLOAD_ERR_OK && !$crt_server_ca_exists) {
+    flash('error', T_('You need a Server CA.'));
+    goto redirect;
+  }
+
+  if(($_FILES['crt_client_key']['error'] != UPLOAD_ERR_OK && (!$crt_client_key_exists || $_POST['crt_client_key_delete'] == 1)) && empty($_POST['login_user'])) {
+    flash('error', T_('You need either a Client Certificate, either a Username (or both).'));
+    goto redirect;
+  }
+
+  if($ip6_net != 'none') {
+    $ip6_net = ipv6_expanded($ip6_net);
+
+    if(empty($ip6_net)) {
+      flash('error', T_('The IPv6 Delegated Prefix format looks bad.'));
+      goto redirect;
+    }
+
+    $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]}:1";
+
+    $ip6_net = ipv6_compressed($ip6_net);
+    $ip6_addr = ipv6_compressed($ip6_addr);
+  }
 
   stop_service();
 
@@ -43,27 +115,30 @@ dispatch_put('/settings', function() {
   moulinette_set('login_user', $_POST['login_user']);
   moulinette_set('login_passphrase', $_POST['login_passphrase']);
   moulinette_set('ip6_net', $ip6_net);
-
-  # TODO: format ip6_net
-  if($ip6_net == 'none') {
-    moulinette_set('ip6_addr', 'none');
-  } else {
-    $ip6_addr = "${ip6_net}1";
-    moulinette_set('ip6_addr', $ip6_addr);
-  }
+  moulinette_set('ip6_addr', $ip6_addr);
 
   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) {
@@ -72,6 +147,7 @@ dispatch_put('/settings', function() {
     flash('error', T_('Configuration updated but service reload failed'));
   }
 
+  redirect:
   redirect_to('/');
 });
 

+ 25 - 0
sources/public/css/style.css

@@ -0,0 +1,25 @@
+a.btn-danger span {
+  color: #eee;
+}
+
+a.btn-danger:hover span {
+  color: #fff;
+}
+
+a.not-allowed {
+  cursor: not-allowed;
+}
+
+input.allowed {
+  cursor: default;
+}
+
+a.btn-disabled, a.btn-disabled:hover, a.btn-disabled:active {
+  background-color: #999;
+  background-image: none;
+  border-color: #888;
+}
+
+a.btn-disabled:hover span {
+  color: #eee;
+}

+ 30 - 2
sources/public/js/custom.js

@@ -3,12 +3,40 @@ $(document).ready(function() {
   $('[data-toggle="tooltip"]').tooltip();
 
   $('.fileinput').click(function() {
-    var realinputid = '#' + $(this).attr('id').replace(/_chooser.*/, '');
-    $(realinputid).click();
+    if(!$(this).hasClass('btn-danger')) {
+      var realinputid = '#' + $(this).attr('id').replace(/_chooser.*/, '');
+
+      $(realinputid).click();
+    }
+  });
+
+  $('.deletefile').click(function() {
+    var chooserbtnid = '#' + $(this).attr('id').replace(/_deletebtn$/, '_chooserbtn');
+    var choosertxtid = '#' + $(this).attr('id').replace(/_deletebtn$/, '_choosertxt');
+    var fileinputid = '#' + $(this).attr('id').replace(/_deletebtn$/, '');
+    var deleteinputid = '#' + $(this).attr('id').replace(/btn$/, '');
+
+    $(deleteinputid).click();
+    $(chooserbtnid).toggleClass('btn-danger');
+    $(chooserbtnid).toggleClass('not-allowed');
+    $(choosertxtid).toggleClass('btn-danger');
+    $(choosertxtid).val($(choosertxtid).hasClass('btn-danger') ? 'Removal requested' : '');
+    $(fileinputid).val('');
+
+    if($(this).attr('id').search('_key') >= 0) {
+      if($(choosertxtid).hasClass('btn-danger') != $('#crt_client_choosertxt').hasClass('btn-danger')) {
+        $('#crt_client_deletebtn').click();
+      }
+    } else {
+      if($(choosertxtid).hasClass('btn-danger') != $('#crt_client_key_choosertxt').hasClass('btn-danger')) {
+        $('#crt_client_key_deletebtn').click();
+      }
+    }
   });
 
   $('input[type="file"]').change(function() {
     var choosertxtid = '#' + $(this).attr('id') + '_choosertxt';
+
     $(choosertxtid).val($(this).val());
   });
 });

+ 37 - 11
sources/views/settings.html.php

@@ -42,9 +42,9 @@
         </div>
       </div>
 
-      <div class="panel panel-success">
+      <div class="panel panel-default">
         <div class="panel-heading">
-          <h3 class="panel-title" data-toggle="tooltip" data-title="<?= T_('Real Internet') ?>"><?= T_("IPv6") ?></h3>
+          <h3 class="panel-title"><?= T_("IPv6") ?></h3>
         </div>
 
         <div style="padding: 14px 14px 0 10px">
@@ -57,6 +57,13 @@
         </div>
       </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">
+          <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-heading">
           <h3 class="panel-title"><?= T_("Certificates") ?></h3>
@@ -64,29 +71,48 @@
 
         <div style="padding: 14px 14px 0 10px">
           <div class="form-group">
-            <label for="crt_client" class="col-sm-3 control-label"><?= T_('Update Client Cert.') ?></label>
+            <label for="crt_client" class="col-sm-3 control-label"><?= $crt_client_exists ? T_('Update Client Cert.') : T_('Upload Client Cert.') ?></label>
             <div class="input-group col-sm-9" style="padding: 0 15px">
-              <input id="crt_client" name="crt_client" type="file" style="display: none" />
+              <?php if($crt_client_exists): ?>
+                <a class="btn btn-danger input-group-addon deletefile" id="crt_client_deletebtn" data-toggle="tooltip" data-title="<?= T_('Delete this certificate') ?>"><span class="glyphicon glyphicon-remove"></span></a>
+                <input id="crt_client_delete" name="crt_client_delete" type="checkbox" value="1" style="display: none" />
+              <?php endif; ?>
               <input type="text" class="form-control fileinput" id="crt_client_choosertxt" placeholder="-----BEGIN CERTIFICATE-----" readonly="readonly" />
-              <a class="btn input-group-addon fileinput" id="crt_client_chooserbtn"><?= T_('Browse') ?></a>
+              <input id="crt_client" name="crt_client" type="file" style="display: none" />
+              <a class="btn input-group-addon fileinput" id="crt_client_chooserbtn" data-toggle="tooltip" data-title="<?= T_('Browse') ?>"><span class="glyphicon glyphicon-search"></span></a>
             </div>
           </div>
 
           <div class="form-group">
-            <label for="crt_client_key" class="col-sm-3 control-label"><?= T_('Update Client Key') ?></label>
+            <label for="crt_client_key" class="col-sm-3 control-label"><?= $crt_client_key_exists ? T_('Update Client Key') : T_('Upload Client Key') ?></label>
             <div class="input-group col-sm-9" style="padding: 0 15px">
-              <input id="crt_client_key" name="crt_client_key" type="file" style="display: none" />
+              <?php if($crt_client_key_exists): ?>
+                <a class="btn btn-danger input-group-addon deletefile" id="crt_client_key_deletebtn" data-toggle="tooltip" data-title="<?= T_('Delete this certificate') ?>"><span class="glyphicon glyphicon-remove"></span></a>
+                <input id="crt_client_key_delete" name="crt_client_key_delete" type="checkbox" value="1" style="display: none" />
+              <?php endif; ?>
               <input type="text" class="form-control fileinput" id="crt_client_key_choosertxt" placeholder="-----BEGIN PRIVATE KEY-----" readonly="readonly" />
-              <a class="btn input-group-addon fileinput" id="crt_client_key_chooserbtn"><?= T_('Browse') ?></a>
+              <input id="crt_client_key" name="crt_client_key" type="file" style="display: none" />
+              <a class="btn input-group-addon fileinput" id="crt_client_key_chooserbtn" data-toggle="tooltip" data-title="<?= T_('Browse') ?>"><span class="glyphicon glyphicon-search"></span></a>
             </div>
           </div>
 
           <div class="form-group">
-            <label for="crt_server_ca" class="col-sm-3 control-label"><?= T_('Update Server CA') ?></label>
+            <?php if(!$crt_server_ca_exists): ?>
+              <div class="alert alert-dismissible alert-warning fade in" style="margin: 2px 16px 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 Server CA for starting your VPN Client.") ?>
+              </div>
+            <?php endif; ?>
+
+            <label for="crt_server_ca" class="col-sm-3 control-label"><?= $crt_server_ca_exists ? T_('Update Server CA') : T_('Upload Server CA') ?></label>
             <div class="input-group col-sm-9" style="padding: 0 15px">
-              <input id="crt_server_ca" name="crt_server_ca" type="file" style="display: none" />
+              <?php if($crt_server_ca_exists): ?>
+                <a class="btn btn-danger not-allowed btn-disabled input-group-addon" id="crt_server_ca_deletebtn" data-toggle="tooltip" data-title="<?= T_('You cannot have no server CA') ?>"><span class="glyphicon glyphicon-remove"></span></a>
+                <input id="crt_server_ca_delete" name="crt_server_ca_delete" type="checkbox" value="1" style="display: none" />
+              <?php endif; ?>
               <input type="text" class="form-control fileinput" id="crt_server_ca_choosertxt" placeholder="-----BEGIN CERTIFICATE-----" readonly="readonly" />
-              <a class="btn input-group-addon fileinput" id="crt_server_ca_chooserbtn"><?= T_('Browse') ?></a>
+              <input id="crt_server_ca" name="crt_server_ca" type="file" style="display: none" />
+              <a class="btn input-group-addon fileinput" id="crt_server_ca_chooserbtn" data-toggle="tooltip" data-title="<?= T_('Browse') ?>"><span class="glyphicon glyphicon-search"></span></a>
             </div>
           </div>
         </div>