Просмотр исходного кода

Feature ssh keys (#838)

* Add support for SSH keys to be added via web interface #701
Co-authored-by: Jaap Marcus <jaap@schipbreukeling.nl>
Raphael Schneeberger 5 лет назад
Родитель
Сommit
896a7c3bcb

+ 1 - 0
CHANGELOG.md

@@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file.
 - Added support for resolving ip addresses based on geoip database for Awstats
 - Added Roundcube plugins newmail_notifier and zipdownload.
 - Added HELO support for multiple domains and IPs.
+- Add the possibility to manage ssh keys in the backend.
 
 ### Bugfixes
 - Do not allow to show apache2 server-status page from public.

+ 65 - 0
bin/v-add-user-ssh-key

@@ -0,0 +1,65 @@
+#!/bin/bash
+# info: add ssh key
+# options: USER key
+#
+# Function check if $user/.ssh/authorized_keys exists and create it
+# After that it append the new key(s)
+
+#----------------------------------------------------------#
+#                    Variable&Function                     #
+#----------------------------------------------------------#
+
+
+# Argument definition
+user=$1
+key=$2
+
+# Includes
+source $HESTIA/func/main.sh
+source $HESTIA/conf/hestia.conf
+
+# Additional argument formatting
+
+#----------------------------------------------------------#
+#                    Verifications                         #
+#----------------------------------------------------------#
+
+is_format_valid 'user'
+is_object_valid 'user' 'USER' "$user"
+is_object_unsuspended 'user' 'USER' "$user"
+
+# Perform verification if read-only mode is enabled
+check_hestia_demo_mode
+
+#----------------------------------------------------------#
+#                       Action                             #
+#----------------------------------------------------------#
+
+# Reading user values
+source $USER_DATA/user.conf
+#check if file exits
+AUTHKEY_FILE=$HOMEDIR/$user/.ssh/authorized_keys
+if [ ! -f "$AUTHKEY_FILE" ]; then
+    touch "$AUTHKEY_FILE"
+    chown ${user}: "${AUTHKEY_FILE}"
+fi
+TEMP=$(mktemp)
+echo "$key" >> "$TEMP"
+ssh-keygen -l -f "$TEMP"
+if [ ! $? -eq 0 ]; then
+    rm "$TEMP"
+    exit
+fi
+rm "$TEMP"
+#append key to file
+echo "$key" >> "$AUTHKEY_FILE"
+
+#----------------------------------------------------------#
+#                       Hestia                             #
+#----------------------------------------------------------#
+
+# Logging
+log_history "added ssh-key $user"
+log_event "$OK" "$ARGUMENTS"
+
+exit

+ 54 - 0
bin/v-delete-user-ssh-key

@@ -0,0 +1,54 @@
+#!/bin/bash
+# info: add ssh key
+# options: USER key
+#
+# Function check if $user/.ssh/authorized_keys exists and create it
+# After that it append the new key(s)
+
+#----------------------------------------------------------#
+#                    Variable&Function                     #
+#----------------------------------------------------------#
+
+# Argument definition
+user=$1
+keyid=$2
+
+# Includes
+source $HESTIA/func/main.sh
+source $HESTIA/conf/hestia.conf
+
+# Additional argument formatting
+
+#----------------------------------------------------------#
+#                    Verifications                         #
+#----------------------------------------------------------#
+
+is_format_valid 'user'
+is_object_valid 'user' 'USER' "$user"
+is_object_unsuspended 'user' 'USER' "$user"
+
+source $USER_DATA/user.conf
+
+FILE=$HOMEDIR/$user/.ssh/authorized_keys
+if [ ! -f "$FILE" ]; then
+    exit;
+fi
+
+# Perform verification if read-only mode is enabled
+check_hestia_demo_mode
+
+#----------------------------------------------------------#
+#                       Action                             #
+#----------------------------------------------------------#
+
+sed -i "/${keyid}/d" "$FILE"
+
+#----------------------------------------------------------#
+#                       Hestia                             #
+#----------------------------------------------------------#
+
+# Logging
+log_history "DELETE ssh-key $user"
+log_event "$OK" "$ARGUMENTS"
+
+exit

+ 111 - 0
bin/v-list-user-ssh-key

@@ -0,0 +1,111 @@
+#!/bin/bash
+# info: add ssh key
+# options: USER 
+#
+# Lists $user/.ssh/authorized_keys
+
+#----------------------------------------------------------#
+#                    Variable&Function                     #
+#----------------------------------------------------------#
+
+
+# Argument definition
+user=$1
+
+format=${2-shell}
+
+# Includes
+source $HESTIA/func/main.sh
+
+# JSON list function
+json_list() {
+    IFS=$'\n'
+    i=1
+    objects=$(echo "$keys" |wc -l)
+    echo "{"
+        for str in $keys; do
+        KEY=$(echo $str | awk '{print $(NF-1)}')
+        ID=$(echo $str | awk '{print $NF}')
+        echo -n '    "'$ID'": {
+        "ID": "'$ID'",
+        "KEY": "'$KEY'"
+        }'
+        if [ "$i" -lt "$objects" ]; then
+            echo ','
+        else
+            echo
+        fi
+        ((i++))
+        done		
+        echo "}"   
+}
+
+shell_list() {
+    IFS=$'\n'
+    echo "ID~KEY"
+    echo "----~----~---"
+    for str in $keys; do
+        KEY=$(echo $str | awk '{print $(NF-1)}')
+        ID=$(echo $str | awk '{print $NF}')
+        echo "$ID~$KEY"
+        done
+}
+
+# PLAIN list function
+plain_list() {
+    IFS=$'\n'
+    for str in $keys; do
+        KEY=$(echo $str | awk '{print $(NF-1)}')
+        ID=$(echo $str | awk '{print $NF}')
+        echo -e "$ID\t$KEY"
+    done
+}
+
+# CSV list function
+csv_list() {
+    IFS=$'\n'
+    echo "ID,KEY"
+    for str in $keys; do
+        KEY=$(echo $str | awk '{print $(NF-1)}')
+        ID=$(echo $str | awk '{print $NF}')
+        echo "\"$ID\",\"$KEY\""
+    done
+}
+
+#----------------------------------------------------------#
+#                    Verifications                         #
+#----------------------------------------------------------#
+
+is_format_valid 'user'
+is_object_valid 'user' 'USER' "$user"
+is_object_unsuspended 'user' 'USER' "$user"
+
+#----------------------------------------------------------#
+#                       Action                             #
+#----------------------------------------------------------#
+
+#check if file exsists
+
+if [ ! -f "$HOMEDIR/$user/.ssh/authorized_keys" ]; then
+    exit
+fi
+# Parsing backup config
+#cat "$HOMEDIR/$user/.ssh/authorized_keys"
+keys=$(cat "$HOMEDIR/$user/.ssh/authorized_keys")
+
+# Listing data
+case $format in
+    json)   json_list ;;
+    plain)  plain_list ;;
+    csv)    csv_list ;;
+    shell)  shell_list |column -t ;;
+esac
+
+
+#----------------------------------------------------------#
+#                       Hestia                             #
+#----------------------------------------------------------#
+
+# Logging
+
+exit

+ 109 - 0
web/add/key/index.php

@@ -0,0 +1,109 @@
+<?php
+error_reporting(E_ALL);
+$TAB = 'USER';
+
+// Main include
+include($_SERVER['DOCUMENT_ROOT']."/inc/main.php");
+
+//check for valid format ssh key. Doesn't check it is working!
+//https://gist.github.com/jupeter/3248095    
+function validateKey($value)
+{
+    $key_parts = explode(' ', $value, 3);
+    if (count($key_parts) < 2) {
+        return false;
+    }
+    if (count($key_parts) > 3) {
+        return false;
+    }
+    
+    $algorithm = $key_parts[0];
+    $key = $key_parts[1];
+    
+    if (!in_array($algorithm, array('ssh-rsa', 'ssh-dss'))) {
+        return false;
+    }
+    
+    $key_base64_decoded = base64_decode($key, true);
+    if ($key_base64_decoded == FALSE) {
+        return false;
+    }
+    
+    $check = base64_decode(substr($key,0,16));
+    $check = preg_replace("/[^\w\-]/","", $check);
+        
+    if((string) $check !== (string) $algorithm) {
+        return false;
+    }
+    return true;
+}
+
+// Check POST request
+if (!empty($_POST['ok'])) {
+    // Check token
+    if ((!isset($_POST['token'])) || ($_SESSION['token'] != $_POST['token'])) {
+        header('location: /login/');
+        exit();
+    }
+    
+    if (empty($_POST['v_key'])){ 
+        $_SESSION['error_msg'] = __('Field SSH_KEY can not be blank.');
+    }
+    
+    if(!$_SESSION['error_msg']){
+        switch ($_POST['v_key']){
+            default: 
+            //key if key already exisits
+            exec (HESTIA_CMD . "v-list-user-ssh-key ".$user." json", $output, $return_var);
+            $data = json_decode(implode('', $output), true);
+            $keylist = array();
+            foreach($data as $key => $value){
+                $idlist[] = trim($data[$key]['ID']);
+                $keylist[] = trim($data[$key]['KEY']);
+            }
+        
+            if(!validateKey($_POST['v_key'])){
+                $_SESSION['error_msg']  = __('SSH KEY is invalid');
+                break;
+            }
+    
+            $v_key_parts = explode(' ',$_POST['v_key']);
+            $key_id = trim($v_key_parts[2]);
+            if($v_key_parts[2] == ''){
+                $_SESSION['error_msg']  = __('SSH KEY is invalid');
+                break;
+            }
+    
+            //for deleting / revoking key the last part user@domain is used therefore needs to be unique
+            //maybe consider adding random generated message or even an human read able string set by user?
+            if(in_array($v_key_parts[2], $idlist)){
+                $_SESSION['error_msg']  =  __('SSH KEY already exists');
+                break;
+            }
+            if(in_array($v_key_parts[1], $keylist)){
+                $_SESSION['error_msg']  =  __('SSH KEY already exists');
+                break;
+            }
+            $v_key = escapeshellarg(trim($_POST['v_key']));
+        }
+    }
+        
+    if (empty($_SESSION['error_msg'])) {
+        exec (HESTIA_CMD."v-add-user-ssh-key ".$user." ".$v_key, $output, $return_var);
+        check_return_code($return_var,$output);
+    }
+
+    unset($output);
+
+    // Flush field values on success
+    if (empty($_SESSION['error_msg'])) {
+    $_SESSION['ok_msg'] = __('SSH KEY created');
+    }
+
+}
+
+render_page($user, $TAB, 'add_key');
+
+// Flush session messages
+unset($_SESSION['error_msg']);
+unset($_SESSION['ok_msg']);    

+ 34 - 0
web/delete/key/index.php

@@ -0,0 +1,34 @@
+<?php
+// Init
+error_reporting(NULL);
+ob_start();
+session_start();
+include($_SERVER['DOCUMENT_ROOT']."/inc/main.php");
+
+if (($_SESSION['user'] == 'admin') && (!empty($_GET['user']))) {
+    $user=$_GET['user'];;
+}
+
+// Check token
+if ((!isset($_GET['token'])) || ($_SESSION['token'] != $_GET['token'])) {
+    header('location: /login/');
+    exit();
+}
+
+if (!empty($_GET['key'])) {
+    $v_key = escapeshellarg(trim($_GET['key']));
+    $v_key = str_replace('/','\\/', $v_key);
+    exec (HESTIA_CMD."v-delete-user-ssh-key ".$user." ".$v_key);
+    check_return_code($return_var,$output);
+}
+
+unset($output);
+
+//die();
+$back = $_SESSION['back'];
+if (!empty($back)) {
+    header("Location: ".$back);
+    exit;
+}
+header("Location: /list/key/");
+exit;

+ 11 - 2
web/inc/i18n/en.php

@@ -770,6 +770,16 @@ $LANG['en'] = array(
     'Please wait while php is installed or removed' => 'Adding or removing a version of PHP will take around 1 minute per version. Please wait until the process has completed and do not refresh the page.',
     'Avoid adding web domains on admin account' => 'It is strongly advised to create a standard user account before adding web domains to the server due to the increased privileges the admin account possesses and potential security risks involved.',
     
+    //SSH Key
+    'Field SSH_KEY can not be blank.' => 'Field SSH Key may not be blank',
+    'SSH KEY is invalid' => 'SSH key is invalid',
+    'SSH KEY already exists' => 'SSH key already exsits',
+    'SSH KEY Added' => 'SSH key has been added',
+    'Add SSH Key' => 'Add SSH key',
+    'SSH KEY' => 'SSH Key',    
+    'DELETE_KEY_CONFIRM' => 'Are you sure you want to delete key %s?',
+    'SSH_ID' => 'SSH Id',
+ 
     //Header 
     'Fm' => 'Files',
     //PHP Cli
@@ -836,6 +846,5 @@ $LANG['en'] = array(
     
     //header
     'Hestia Control Panel' => 'Hestia Control Panel',
-    
-    
+
 );

+ 17 - 0
web/list/key/index.php

@@ -0,0 +1,17 @@
+<?php
+error_reporting(NULL);
+$TAB = 'USER';
+
+// Main include
+include($_SERVER['DOCUMENT_ROOT']."/inc/main.php");
+
+exec (HESTIA_CMD . "v-list-user-ssh-key ".escapeshellarg($user)." json", $output, $return_var);
+
+$data = json_decode(implode('', $output), true);
+
+// Render page\
+render_page($user, $TAB, 'list_key');
+
+// Back uri
+$_SESSION['back'] = $_SERVER['REQUEST_URI'];
+?>

+ 72 - 0
web/templates/admin/add_key.html

@@ -0,0 +1,72 @@
+	<div class="l-center edit">
+        <div class="l-sort clearfix">
+          <div class="l-unit-toolbar__buttonstrip">
+            <a class="ui-button cancel" id="btn-back" href="/list/key/"><i class="fas fa-arrow-left status-icon blue"></i> <?=__('Back')?></a>
+          </div>
+          <div class="l-unit-toolbar__buttonstrip float-right">
+            <a href="#" class="ui-button" title="<?=__('Save')?>" data-action="submit" data-id="vstobjects"><i class="fas fa-save status-icon purple"></i> <?=__('Save')?></a>
+          </div>
+        </div>
+      </div>
+
+    <div class="l-separator"></div>
+    <!-- /.l-separator -->
+	
+    <div class="l-center animated fadeIn">
+        <?php
+          $back = $_SESSION['back'];
+          if (empty($back)) {
+            $back = "location.href='/list/key/'";
+          } else {
+            $back = "location.href='".$back."'";
+          }
+        ?>
+        <form id="vstobjects" name="v_add_key" method="post">
+            <input type="hidden" name="token" value="<?=$_SESSION['token']?>" />
+            <input type="hidden" name="ok" value="Add" />
+
+            <table class="data mode-add">
+                <tr class="data-add">
+                    <td class="data-dotted">
+                        <table class="data-col1">
+                            <tr><td></td></tr>
+                        </table>
+                    </td>
+                    <td class="data-dotted">
+                        <table class="data-col2" width="600px">
+                            <tr>
+                                <td class="step-top">
+                                    <span class="page-title"><?=__('Add SSH Key')?></span>
+                                </td>
+                            </tr>
+                            <tr>
+                                <td>
+                                    <?php
+                                        if (!empty($_SESSION['error_msg'])) {
+                                            echo "<span class=\"vst-error\"> <i class=\"fas fa-exclamation-circle status-icon red\"></i> ".htmlentities($_SESSION['error_msg'])."</span>";
+                                        } else {
+                                            if (!empty($_SESSION['ok_msg'])) {
+                                                echo "<span class=\"vst-ok\"> <i class=\"fas fa-check-circle status-icon green\"></i> ".$_SESSION['ok_msg']."</span>";
+                                            }
+                                        }
+                                    ?>
+                                </td>
+                            </tr>
+                            <tr>
+                                <td class="vst-text step-top">
+                                    <?php print __('SSH KEY') ?>
+                                </td>
+                            </tr>
+                            <tr>
+                                <td>
+                                    <textarea class="vst-textinput" name="v_key"><?=htmlentities(trim($v_key, "'"))?></textarea>
+                                </td>
+                            </tr>
+                        </table>
+                        <table class="data-col2">
+                        </table>
+                    </td>
+                </tr>
+            </table>
+        </form>
+      </div>

+ 69 - 0
web/templates/admin/list_key.html

@@ -0,0 +1,69 @@
+    <div class="l-center">
+      <div class="l-sort clearfix noselect">
+        <div class="l-unit-toolbar__buttonstrip">
+        <?php
+        	 echo '<a href="/add/key/" id="btn-create" class="ui-button cancel" title="'.__('Add SSH Key').'"><i class="fas fa-plus-circle status-icon green"></i>'.__('Add SSH Key').'</a>';
+        ?>
+      </div>
+     </div>
+    </div>
+    
+<div class="l-separator"></div>
+
+<div class="l-center units animated fadeIn">
+  <div class="header table-header">     
+    <div class="l-unit__col l-unit__col--right">
+      <div class="clearfix l-unit__stat-col--left wide-3"><b><?php print __('SSH_ID');?></b></div>
+      <div class="clearfix l-unit__stat-col--left compact-2">
+        &nbsp;
+      </div> 
+      <div class="clearfix l-unit__stat-col--left wide-7"><b><?php print __('SSH KEY');?></b></div>
+    </div>
+  </div>
+
+ <?php
+ 	$i = 0;
+    foreach ($data as $key => $value) {
+    ++$i;
+  ?>
+    <div class="l-unit header">
+      <div class="l-unit__col l-unit__col--right">
+        <div class="clearfix l-unit__stat-col--left wide-3"><b><?=$data[$key]['ID'];?></b></div>
+            <div class="clearfix l-unit__stat-col--left text-left compact-2">
+                <div class="l-unit-toolbar__col l-unit-toolbar__col--right noselect">
+                    <div class="actions-panel clearfix">
+                        <div class="actions-panel__col actions-panel__delete shortcut-delete" key-action="js">
+                            <a id="delete_link_<?=$i?>" class="data-controls do_delete" title="<?=__('delete')?>">
+                                <i class="fas fa-trash status-icon red status-icon dim do_delete"></i>
+                                <input type="hidden" name="delete_url" value="/delete/key/?key=<?=$key?>&token=<?=$_SESSION['token']?>" />
+                                <div id="delete_dialog_<?=$i?>" class="confirmation-text-delete hidden" title="<?=__('Confirmation')?>">
+                                    <p class="confirmation"><?=__('DELETE_KEY_CONFIRM',$key)?></p>
+                                </div>
+                            </a>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        <div class="clearfix l-unit__stat-col--left wide-7"><b><?=htmlspecialchars(substr($data[$key]['KEY'],0,50).'.....'.substr($data[$key]['ID'], -10,10), ENT_QUOTES);?></b></div>
+      </div>
+    </div>
+  <?}?>
+</div>
+
+<div id="vstobjects">
+    <div class="l-separator"></div>
+        <div class="l-center">
+            <div class="l-unit-ft">
+                <table class='data'></table>
+                <div class="data-count l-unit__col l-unit__col--right clearfix">
+                <?php if ( $i == 1) {
+                    echo __('1 SSH Key');
+                } else {
+                    echo __('%s SSH Keys',$i);
+                }
+        ?>
+      </div>
+    </div>
+  </div>
+</div>
+