Skip to content

Commit 896a7c3

Browse files
Feature ssh keys (hestiacp#838)
* Add support for SSH keys to be added via web interface hestiacp#701 Co-authored-by: Jaap Marcus <jaap@schipbreukeling.nl>
1 parent 1718ccd commit 896a7c3

File tree

10 files changed

+543
-2
lines changed

10 files changed

+543
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file.
99
- Added support for resolving ip addresses based on geoip database for Awstats
1010
- Added Roundcube plugins newmail_notifier and zipdownload.
1111
- Added HELO support for multiple domains and IPs.
12+
- Add the possibility to manage ssh keys in the backend.
1213

1314
### Bugfixes
1415
- Do not allow to show apache2 server-status page from public.

bin/v-add-user-ssh-key

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#!/bin/bash
2+
# info: add ssh key
3+
# options: USER key
4+
#
5+
# Function check if $user/.ssh/authorized_keys exists and create it
6+
# After that it append the new key(s)
7+
8+
#----------------------------------------------------------#
9+
# Variable&Function #
10+
#----------------------------------------------------------#
11+
12+
13+
# Argument definition
14+
user=$1
15+
key=$2
16+
17+
# Includes
18+
source $HESTIA/func/main.sh
19+
source $HESTIA/conf/hestia.conf
20+
21+
# Additional argument formatting
22+
23+
#----------------------------------------------------------#
24+
# Verifications #
25+
#----------------------------------------------------------#
26+
27+
is_format_valid 'user'
28+
is_object_valid 'user' 'USER' "$user"
29+
is_object_unsuspended 'user' 'USER' "$user"
30+
31+
# Perform verification if read-only mode is enabled
32+
check_hestia_demo_mode
33+
34+
#----------------------------------------------------------#
35+
# Action #
36+
#----------------------------------------------------------#
37+
38+
# Reading user values
39+
source $USER_DATA/user.conf
40+
#check if file exits
41+
AUTHKEY_FILE=$HOMEDIR/$user/.ssh/authorized_keys
42+
if [ ! -f "$AUTHKEY_FILE" ]; then
43+
touch "$AUTHKEY_FILE"
44+
chown ${user}: "${AUTHKEY_FILE}"
45+
fi
46+
TEMP=$(mktemp)
47+
echo "$key" >> "$TEMP"
48+
ssh-keygen -l -f "$TEMP"
49+
if [ ! $? -eq 0 ]; then
50+
rm "$TEMP"
51+
exit
52+
fi
53+
rm "$TEMP"
54+
#append key to file
55+
echo "$key" >> "$AUTHKEY_FILE"
56+
57+
#----------------------------------------------------------#
58+
# Hestia #
59+
#----------------------------------------------------------#
60+
61+
# Logging
62+
log_history "added ssh-key $user"
63+
log_event "$OK" "$ARGUMENTS"
64+
65+
exit

bin/v-delete-user-ssh-key

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#!/bin/bash
2+
# info: add ssh key
3+
# options: USER key
4+
#
5+
# Function check if $user/.ssh/authorized_keys exists and create it
6+
# After that it append the new key(s)
7+
8+
#----------------------------------------------------------#
9+
# Variable&Function #
10+
#----------------------------------------------------------#
11+
12+
# Argument definition
13+
user=$1
14+
keyid=$2
15+
16+
# Includes
17+
source $HESTIA/func/main.sh
18+
source $HESTIA/conf/hestia.conf
19+
20+
# Additional argument formatting
21+
22+
#----------------------------------------------------------#
23+
# Verifications #
24+
#----------------------------------------------------------#
25+
26+
is_format_valid 'user'
27+
is_object_valid 'user' 'USER' "$user"
28+
is_object_unsuspended 'user' 'USER' "$user"
29+
30+
source $USER_DATA/user.conf
31+
32+
FILE=$HOMEDIR/$user/.ssh/authorized_keys
33+
if [ ! -f "$FILE" ]; then
34+
exit;
35+
fi
36+
37+
# Perform verification if read-only mode is enabled
38+
check_hestia_demo_mode
39+
40+
#----------------------------------------------------------#
41+
# Action #
42+
#----------------------------------------------------------#
43+
44+
sed -i "/${keyid}/d" "$FILE"
45+
46+
#----------------------------------------------------------#
47+
# Hestia #
48+
#----------------------------------------------------------#
49+
50+
# Logging
51+
log_history "DELETE ssh-key $user"
52+
log_event "$OK" "$ARGUMENTS"
53+
54+
exit

bin/v-list-user-ssh-key

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#!/bin/bash
2+
# info: add ssh key
3+
# options: USER
4+
#
5+
# Lists $user/.ssh/authorized_keys
6+
7+
#----------------------------------------------------------#
8+
# Variable&Function #
9+
#----------------------------------------------------------#
10+
11+
12+
# Argument definition
13+
user=$1
14+
15+
format=${2-shell}
16+
17+
# Includes
18+
source $HESTIA/func/main.sh
19+
20+
# JSON list function
21+
json_list() {
22+
IFS=$'\n'
23+
i=1
24+
objects=$(echo "$keys" |wc -l)
25+
echo "{"
26+
for str in $keys; do
27+
KEY=$(echo $str | awk '{print $(NF-1)}')
28+
ID=$(echo $str | awk '{print $NF}')
29+
echo -n ' "'$ID'": {
30+
"ID": "'$ID'",
31+
"KEY": "'$KEY'"
32+
}'
33+
if [ "$i" -lt "$objects" ]; then
34+
echo ','
35+
else
36+
echo
37+
fi
38+
((i++))
39+
done
40+
echo "}"
41+
}
42+
43+
shell_list() {
44+
IFS=$'\n'
45+
echo "ID~KEY"
46+
echo "----~----~---"
47+
for str in $keys; do
48+
KEY=$(echo $str | awk '{print $(NF-1)}')
49+
ID=$(echo $str | awk '{print $NF}')
50+
echo "$ID~$KEY"
51+
done
52+
}
53+
54+
# PLAIN list function
55+
plain_list() {
56+
IFS=$'\n'
57+
for str in $keys; do
58+
KEY=$(echo $str | awk '{print $(NF-1)}')
59+
ID=$(echo $str | awk '{print $NF}')
60+
echo -e "$ID\t$KEY"
61+
done
62+
}
63+
64+
# CSV list function
65+
csv_list() {
66+
IFS=$'\n'
67+
echo "ID,KEY"
68+
for str in $keys; do
69+
KEY=$(echo $str | awk '{print $(NF-1)}')
70+
ID=$(echo $str | awk '{print $NF}')
71+
echo "\"$ID\",\"$KEY\""
72+
done
73+
}
74+
75+
#----------------------------------------------------------#
76+
# Verifications #
77+
#----------------------------------------------------------#
78+
79+
is_format_valid 'user'
80+
is_object_valid 'user' 'USER' "$user"
81+
is_object_unsuspended 'user' 'USER' "$user"
82+
83+
#----------------------------------------------------------#
84+
# Action #
85+
#----------------------------------------------------------#
86+
87+
#check if file exsists
88+
89+
if [ ! -f "$HOMEDIR/$user/.ssh/authorized_keys" ]; then
90+
exit
91+
fi
92+
# Parsing backup config
93+
#cat "$HOMEDIR/$user/.ssh/authorized_keys"
94+
keys=$(cat "$HOMEDIR/$user/.ssh/authorized_keys")
95+
96+
# Listing data
97+
case $format in
98+
json) json_list ;;
99+
plain) plain_list ;;
100+
csv) csv_list ;;
101+
shell) shell_list |column -t ;;
102+
esac
103+
104+
105+
#----------------------------------------------------------#
106+
# Hestia #
107+
#----------------------------------------------------------#
108+
109+
# Logging
110+
111+
exit

web/add/key/index.php

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?php
2+
error_reporting(E_ALL);
3+
$TAB = 'USER';
4+
5+
// Main include
6+
include($_SERVER['DOCUMENT_ROOT']."/inc/main.php");
7+
8+
//check for valid format ssh key. Doesn't check it is working!
9+
//https://gist.github.com/jupeter/3248095
10+
function validateKey($value)
11+
{
12+
$key_parts = explode(' ', $value, 3);
13+
if (count($key_parts) < 2) {
14+
return false;
15+
}
16+
if (count($key_parts) > 3) {
17+
return false;
18+
}
19+
20+
$algorithm = $key_parts[0];
21+
$key = $key_parts[1];
22+
23+
if (!in_array($algorithm, array('ssh-rsa', 'ssh-dss'))) {
24+
return false;
25+
}
26+
27+
$key_base64_decoded = base64_decode($key, true);
28+
if ($key_base64_decoded == FALSE) {
29+
return false;
30+
}
31+
32+
$check = base64_decode(substr($key,0,16));
33+
$check = preg_replace("/[^\w\-]/","", $check);
34+
35+
if((string) $check !== (string) $algorithm) {
36+
return false;
37+
}
38+
return true;
39+
}
40+
41+
// Check POST request
42+
if (!empty($_POST['ok'])) {
43+
// Check token
44+
if ((!isset($_POST['token'])) || ($_SESSION['token'] != $_POST['token'])) {
45+
header('location: /login/');
46+
exit();
47+
}
48+
49+
if (empty($_POST['v_key'])){
50+
$_SESSION['error_msg'] = __('Field SSH_KEY can not be blank.');
51+
}
52+
53+
if(!$_SESSION['error_msg']){
54+
switch ($_POST['v_key']){
55+
default:
56+
//key if key already exisits
57+
exec (HESTIA_CMD . "v-list-user-ssh-key ".$user." json", $output, $return_var);
58+
$data = json_decode(implode('', $output), true);
59+
$keylist = array();
60+
foreach($data as $key => $value){
61+
$idlist[] = trim($data[$key]['ID']);
62+
$keylist[] = trim($data[$key]['KEY']);
63+
}
64+
65+
if(!validateKey($_POST['v_key'])){
66+
$_SESSION['error_msg'] = __('SSH KEY is invalid');
67+
break;
68+
}
69+
70+
$v_key_parts = explode(' ',$_POST['v_key']);
71+
$key_id = trim($v_key_parts[2]);
72+
if($v_key_parts[2] == ''){
73+
$_SESSION['error_msg'] = __('SSH KEY is invalid');
74+
break;
75+
}
76+
77+
//for deleting / revoking key the last part user@domain is used therefore needs to be unique
78+
//maybe consider adding random generated message or even an human read able string set by user?
79+
if(in_array($v_key_parts[2], $idlist)){
80+
$_SESSION['error_msg'] = __('SSH KEY already exists');
81+
break;
82+
}
83+
if(in_array($v_key_parts[1], $keylist)){
84+
$_SESSION['error_msg'] = __('SSH KEY already exists');
85+
break;
86+
}
87+
$v_key = escapeshellarg(trim($_POST['v_key']));
88+
}
89+
}
90+
91+
if (empty($_SESSION['error_msg'])) {
92+
exec (HESTIA_CMD."v-add-user-ssh-key ".$user." ".$v_key, $output, $return_var);
93+
check_return_code($return_var,$output);
94+
}
95+
96+
unset($output);
97+
98+
// Flush field values on success
99+
if (empty($_SESSION['error_msg'])) {
100+
$_SESSION['ok_msg'] = __('SSH KEY created');
101+
}
102+
103+
}
104+
105+
render_page($user, $TAB, 'add_key');
106+
107+
// Flush session messages
108+
unset($_SESSION['error_msg']);
109+
unset($_SESSION['ok_msg']);

web/delete/key/index.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
// Init
3+
error_reporting(NULL);
4+
ob_start();
5+
session_start();
6+
include($_SERVER['DOCUMENT_ROOT']."/inc/main.php");
7+
8+
if (($_SESSION['user'] == 'admin') && (!empty($_GET['user']))) {
9+
$user=$_GET['user'];;
10+
}
11+
12+
// Check token
13+
if ((!isset($_GET['token'])) || ($_SESSION['token'] != $_GET['token'])) {
14+
header('location: /login/');
15+
exit();
16+
}
17+
18+
if (!empty($_GET['key'])) {
19+
$v_key = escapeshellarg(trim($_GET['key']));
20+
$v_key = str_replace('/','\\/', $v_key);
21+
exec (HESTIA_CMD."v-delete-user-ssh-key ".$user." ".$v_key);
22+
check_return_code($return_var,$output);
23+
}
24+
25+
unset($output);
26+
27+
//die();
28+
$back = $_SESSION['back'];
29+
if (!empty($back)) {
30+
header("Location: ".$back);
31+
exit;
32+
}
33+
header("Location: /list/key/");
34+
exit;

0 commit comments

Comments
 (0)