Explorar el Código

Implementation of jailed shells (#4052)

* feat: Initial implementation of jailed bash shell

* Remove error messages if jail is not installed

Jail not being installed is a expected default state. This way a
user that wants a non-jailed bash shell has the ability to keep
using those.

* Fix error with nologin chroot not being kept intact and add bash jail test

* Make sure the group and bash shell is added to the jail

* Make hestia depend on jailkit

* allow admin to enable jail for packages and users

* Make sure jail is created correctly

* Remove some duplicate calls and add some more of explanation

* Add more explanation on why changes can be reverted

* Move homedir in chroot and fix issues with add and remove

To make ssh jails work properly we need to move the homedir to the
same root of the jail as the original location. We also need php
and a user writable /tmp in the jail.

* fix tests to show the new mount location

* fix small issues with conf not being read properly

* install all server php versions in the user jail and remove fpm

* Add validation for some unwanted shell with jail combinations

* Add unzip, zip and zstd to jail

* remove cli docs reference

* fix spacing in v-list-user

* make default jail enabled no

* change way ssl certificates are handled

* fixed jail errors for groups and added node and npm
Robert-Jan de Dreu hace 2 años
padre
commit
417db773f8

+ 111 - 0
bin/v-add-sys-ssh-jail

@@ -0,0 +1,111 @@
+#!/bin/bash
+# info: add system ssh jail
+# options: [RESTART]
+#
+# example: v-add-sys-ssh-jail yes
+#
+# This function enables ssh jailed environment.
+
+#----------------------------------------------------------#
+#                Variables & Functions                     #
+#----------------------------------------------------------#
+
+# Includes
+# shellcheck source=/etc/hestiacp/hestia.conf
+source /etc/hestiacp/hestia.conf
+# shellcheck source=/usr/local/hestia/func/main.sh
+source $HESTIA/func/main.sh
+# load config file
+source_conf "$HESTIA/conf/hestia.conf"
+
+restart=$1
+
+#----------------------------------------------------------#
+#                    Verifications                         #
+#----------------------------------------------------------#
+
+# Checking if jailkit is installed
+if [ ! -x /sbin/jk_init ]; then
+	exit
+fi
+
+# Perform verification if read-only mode is enabled
+check_hestia_demo_mode
+
+#----------------------------------------------------------#
+#                       Action                             #
+#----------------------------------------------------------#
+
+# Checking sshd directives
+config='/etc/ssh/sshd_config'
+ssh_i=$(grep -n "^# Hestia SSH Chroot" $config)
+
+# Enabling jailed ssh
+if [ -z "$ssh_i" ]; then
+	echo " " >> $config
+	echo "# Hestia SSH Chroot" >> $config
+	echo "Match Group ssh-jailed" >> $config
+	echo "    ChrootDirectory /srv/jail/%u" >> $config
+	echo "    X11Forwarding no" >> $config
+	echo "    AllowTCPForwarding no" >> $config
+	restart='yes'
+fi
+
+# Validating opensshd config
+if [ "$restart" = 'yes' ]; then
+	subj="OpenSSH restart failed"
+	email=$(grep CONTACT "$HESTIA/data/users/$ROOT_USER/user.conf" | cut -f 2 -d \')
+	/usr/sbin/sshd -t > /dev/null 2>&1
+	if [ "$?" -ne 0 ]; then
+		mail_text="OpenSSH can not be restarted. Please check config:
+            \n\n$(/usr/sbin/sshd -t)"
+		echo -e "$mail_text" | $SENDMAIL -s "$subj" $email
+	else
+		service sshd restart > /dev/null 2>&1
+	fi
+fi
+
+# Adding group
+groupadd ssh-jailed 2> /dev/null
+
+# Checking jailkit init
+jk_init='/etc/jailkit/jk_init.ini'
+jk_php_i=$(grep -n "^# Hestia Jail Settings" $jk_init)
+
+# Add PHP to jailkit init to allow usage of it within jail
+if [ -z "$jk_php_i" ]; then
+	cp -f $HESTIA_COMMON_DIR/jailkit/jk_init.ini /etc/jailkit
+fi
+
+# Restart ssh service
+if [ "$restart" = 'no' ]; then
+	# Skip restart of SSH daemon
+	echo "" > /dev/null 2>&1
+else
+	service ssh restart > /dev/null 2>&1
+fi
+
+# Jails need maintenance to update the binaries within the jail. To do so we just reset the chroot
+# and reapply the jail
+for user in $("$BIN/v-list-users" list); do
+	check_jail_enabled=$(grep "SHELL_JAIL_ENABLED='yes'" $HESTIA/data/users/$user/user.conf)
+
+	# If jail enabled try to jail the user
+	if [ -n "$check_jail_enabled" ]; then
+		$BIN/v-add-user-ssh-jail "$user" "no"
+	fi
+done
+
+# Add v-add-sys-ssh-jail to startup
+if [ ! -e "/etc/cron.d/hestia-ssh-jail" ]; then
+	echo "@reboot root sleep 60 && /usr/local/hestia/bin/v-add-sys-ssh-jail > /dev/null" > /etc/cron.d/hestia-ssh-jail
+fi
+
+#----------------------------------------------------------#
+#                       Hestia                             #
+#----------------------------------------------------------#
+
+# Logging
+log_event "$OK" "$ARGUMENTS"
+
+exit

+ 6 - 1
bin/v-add-user

@@ -69,7 +69,7 @@ check_hestia_demo_mode
 pkg_data=$(cat $HESTIA/data/packages/$package.pkg | egrep -v "TIME|DATE")
 
 # Checking shell
-shell_conf=$(echo "$pkg_data" | grep 'SHELL' | cut -f 2 -d \')
+shell_conf=$(echo "$pkg_data" | grep -m1 'SHELL' | cut -f 2 -d \')
 shell=$(grep -w "$shell_conf" /etc/shells | head -n1)
 
 # Adding user
@@ -274,6 +274,11 @@ fi
 # Adding jailed sftp env
 $BIN/v-add-user-sftp-jail $user
 
+# Adding jailed ssh env
+if [ "$SHELL_JAIL_ENABLED" = 'yes' ]; then
+	$BIN/v-add-user-ssh-jail $user
+fi
+
 #----------------------------------------------------------#
 #                       Hestia                             #
 #----------------------------------------------------------#

+ 2 - 0
bin/v-add-user-package

@@ -81,6 +81,7 @@ is_package_consistent() {
 	fi
 
 	is_format_valid_shell "$SHELL"
+	is_boolean_format_valid "$SHELL_JAIL_ENABLED" 'SHELL_JAIL_ENABLED'
 }
 
 #----------------------------------------------------------#
@@ -132,6 +133,7 @@ DISK_QUOTA='$DISK_QUOTA'
 BANDWIDTH='$BANDWIDTH'
 NS='$NS'
 SHELL='$SHELL'
+SHELL_JAIL_ENABLED='$SHELL_JAIL_ENABLED'
 BACKUPS='$BACKUPS'
 TIME='$time'
 DATE='$date'

+ 96 - 0
bin/v-add-user-ssh-jail

@@ -0,0 +1,96 @@
+#!/bin/bash
+# info: add user ssh jail
+# options: USER [RESTART]
+#
+# example: v-add-user-ssh-jail admin
+#
+# This function enables ssh jailed environment
+
+#----------------------------------------------------------#
+#                Variables & Functions                     #
+#----------------------------------------------------------#
+
+# Argument definition
+user=$1
+restart=$3
+
+# Includes
+# shellcheck source=/etc/hestiacp/hestia.conf
+source /etc/hestiacp/hestia.conf
+# shellcheck source=/usr/local/hestia/func/main.sh
+source $HESTIA/func/main.sh
+# load config file
+source_conf "$HESTIA/conf/hestia.conf"
+
+chroot="/srv/jail/$user"
+
+#----------------------------------------------------------#
+#                    Verifications                         #
+#----------------------------------------------------------#
+
+# Checking if jailkit is installed
+if [ ! -x /sbin/jk_init ]; then
+	exit
+fi
+
+check_args '1' "$#" 'USER'
+is_format_valid 'user'
+
+# Perform verification if read-only mode is enabled
+check_hestia_demo_mode
+
+#----------------------------------------------------------#
+#                       Action                             #
+#----------------------------------------------------------#
+
+# Get shell full path
+shell_path=$(grep "^$user:" /etc/passwd | cut -f 7 -d :)
+
+# Set home folder permission to root
+if [ -d "/home/$user" ]; then
+	chown root:root /home/$user
+fi
+
+add_chroot_jail "$user"
+
+# Add user to the ssh-jailed group to allow jailed ssh
+# This needs to be done first to make sure these groups are made available in the jail
+usermod -a -G ssh-jailed $user
+
+# Installing shell files into the user chroot directory
+# - IMPORTANT - MODIFY THE FOLLOWING LINES AND THE FILE jk_init.ini ACCORDING TO YOUR SYSTEM AND YOUR PREFERENCES
+/sbin/jk_init -f -j $chroot extendedshell netutils ssh sftp scp git php php5_6 php7_0 php7_1 php7_2 php7_3 php7_4 php8_0 php8_1 php8_2 > /dev/null 2>&1
+/sbin/jk_cp -f -j $chroot /bin/id > /dev/null 2>&1
+
+# Jailing user to make sure passwd and groups are set correctly within the jail.
+# This command also does a little too much by changing the users homedir and
+# shell in /etc/passwd. The next commands reverts those changes for compatibility
+# with hestia.
+/sbin/jk_jailuser -n -s $shell_path -j $chroot $user
+
+# Reset home directory and shell again for hestiacp because jailkit changes these.
+# Normally these are needed to redirect the ssh user to it's chroot but because we
+# use a custom sshd_config to redirect the user to it's chroot we don't need it to be
+# changed in /etc/passwd for the user.
+usermod -d /home/$user $user
+usermod -s $shell_path $user
+
+#----------------------------------------------------------#
+#                       Hestia                             #
+#----------------------------------------------------------#
+
+# Enabling user jail
+update_user_value "$user" '$SHELL_JAIL_ENABLED' "yes"
+
+# Restart ssh service
+if [ "$restart" = 'no' ]; then
+	# Skip restart of SSH daemon
+	echo "" > /dev/null 2>&1
+else
+	service sshd restart > /dev/null 2>&1
+fi
+
+# Logging
+log_event "$OK" "$ARGUMENTS"
+
+exit

+ 4 - 2
bin/v-change-user-package

@@ -99,6 +99,7 @@ DISK_QUOTA='$DISK_QUOTA'
 BANDWIDTH='$BANDWIDTH'
 NS='$NS'
 SHELL='$SHELL'
+SHELL_JAIL_ENABLED='$SHELL_JAIL_ENABLED'
 BACKUPS='$BACKUPS'
 CONTACT='$CONTACT'
 CRON_REPORTS='$CRON_REPORTS'
@@ -169,8 +170,9 @@ check_hestia_demo_mode
 change_user_package
 
 # Update user shell
-shell_conf=$(cat "$HESTIA/data/packages/$package.pkg" | grep 'SHELL' | cut -f 2 -d \')
-$BIN/v-change-user-shell "$user" "$shell_conf"
+shell_conf=$(cat "$HESTIA/data/packages/$package.pkg" | grep -m1 'SHELL' | cut -f 2 -d \')
+shell_jail_enabled_conf=$(cat "$HESTIA/data/packages/$package.pkg" | grep 'SHELL_JAIL_ENABLED' | cut -f 2 -d \')
+$BIN/v-change-user-shell "$user" "$shell_conf" "$shell_jail_enabled_conf"
 
 # Run template trigger
 if [ -x "$HESTIA/data/packages/$package.sh" ]; then

+ 12 - 4
bin/v-change-user-shell

@@ -1,8 +1,8 @@
 #!/bin/bash
 # info: change user shell
-# options: USER SHELL
+# options: USER SHELL JAIL_ENABLED
 #
-# example: v-change-user-shell admin nologin
+# example: v-change-user-shell admin nologin no
 #
 # This function changes system shell of a user. Shell gives ability to use ssh.
 
@@ -13,6 +13,7 @@
 # Argument definition
 user=$1
 shell=$2
+shell_jail_enabled=${3-no}
 
 # Includes
 # shellcheck source=/etc/hestiacp/hestia.conf
@@ -26,8 +27,8 @@ source $HESTIA/conf/hestia.conf
 #                    Verifications                         #
 #----------------------------------------------------------#
 
-check_args '2' "$#" 'USER SHELL'
-is_format_valid 'user' 'shell'
+check_args '3' "$#" 'USER SHELL SHELL_JAIL_ENABLED'
+is_format_valid 'user' 'shell shell_jail_enabled'
 is_object_valid 'user' 'USER' "$user"
 is_object_unsuspended 'user' 'USER' "$user"
 
@@ -52,6 +53,13 @@ else
 	$BIN/v-delete-user-sftp-jail "$user" > /dev/null 2>&1
 fi
 
+# Adding jailed ssh env
+if [[ "$shell_jail_enabled" =~ yes ]]; then
+	$BIN/v-add-user-ssh-jail "$user" > /dev/null 2>&1
+else
+	$BIN/v-delete-user-ssh-jail "$user" > /dev/null 2>&1
+fi
+
 #----------------------------------------------------------#
 #                       Hestia                             #
 #----------------------------------------------------------#

+ 72 - 0
bin/v-delete-sys-ssh-jail

@@ -0,0 +1,72 @@
+#!/bin/bash
+# info: delete system ssh jail
+# options: NONE
+#
+# example: v-delete-sys-ssh-jail
+#
+# This function disables ssh jailed environment
+
+#----------------------------------------------------------#
+#                Variables & Functions                     #
+#----------------------------------------------------------#
+
+# Includes
+# shellcheck source=/etc/hestiacp/hestia.conf
+source /etc/hestiacp/hestia.conf
+# shellcheck source=/usr/local/hestia/func/main.sh
+source $HESTIA/func/main.sh
+# load config file
+source_conf "$HESTIA/conf/hestia.conf"
+
+#----------------------------------------------------------#
+#                    Verifications                         #
+#----------------------------------------------------------#
+
+# Perform verification if read-only mode is enabled
+check_hestia_demo_mode
+
+#----------------------------------------------------------#
+#                       Action                             #
+#----------------------------------------------------------#
+
+# Checking sshd directives
+config='/etc/ssh/sshd_config'
+ssh_i=$(grep -n "^# Hestia SSH Chroot" $config)
+
+# Backing up config
+cp $config $config.bak
+
+# Disabling jailed ssh
+if [ -n "$ssh_i" ]; then
+	fline=$(echo "$ssh_i" | cut -f 1 -d :)
+	lline=$((fline + 4))
+	sed -i "${fline},${lline}d" $config
+	restart='yes'
+fi
+
+# Validating opensshd config
+if [ "$restart" = 'yes' ]; then
+	subj="OpenSSH restart failed"
+	email=$(grep CONTACT "$HESTIA/data/users/$ROOT_USER/user.conf" | cut -f 2 -d \')
+	/usr/sbin/sshd -t > /dev/null 2>&1
+	if [ "$?" -ne 0 ]; then
+		mail_text="OpenSSH can not be restarted. Please check config:
+            \n\n$(/usr/sbin/sshd -t)"
+		echo -e "$mail_text" | $SENDMAIL -s "$subj" $email
+	else
+		service sshd restart > /dev/null 2>&1
+	fi
+fi
+
+# Remove group ssh-jailed
+groupdel ssh-jailed 2> /dev/null
+
+#----------------------------------------------------------#
+#                       Hestia                             #
+#----------------------------------------------------------#
+
+# Logging
+$BIN/v-log-action "system" "Warning" "Plugins" "SSH Chroot Jail disabled."
+log_event "$OK" "$ARGUMENTS"
+
+exit

+ 3 - 0
bin/v-delete-user

@@ -89,6 +89,9 @@ sed -i "/ $user$/d" "$HESTIA/data/queue/traffic.pipe"
 # Deleting sftp jail
 $BIN/v-delete-user-sftp-jail "$user"
 
+# Deleting ssh jail
+$BIN/v-delete-user-ssh-jail "$user"
+
 # Deleting system user
 /usr/sbin/userdel -f "$user" >> /dev/null 2>&1
 if [ $? -ne 0 ]; then

+ 71 - 0
bin/v-delete-user-ssh-jail

@@ -0,0 +1,71 @@
+#!/bin/bash
+# info: delete user ssh jail
+# options: USER
+#
+# example: v-delete-user-ssh-jail whistler
+#
+# This function disables ssh jailed environment for USER
+
+#----------------------------------------------------------#
+#                Variables & Functions                     #
+#----------------------------------------------------------#
+
+# Argument definition
+user=$1
+
+# Includes
+# shellcheck source=/etc/hestiacp/hestia.conf
+source /etc/hestiacp/hestia.conf
+# shellcheck source=/usr/local/hestia/func/main.sh
+source $HESTIA/func/main.sh
+# load config file
+source_conf "$HESTIA/conf/hestia.conf"
+
+#----------------------------------------------------------#
+#                    Verifications                         #
+#----------------------------------------------------------#
+
+check_args '1' "$#" 'USER'
+is_format_valid 'user'
+user_str=$(grep "^$user:" /etc/passwd)
+if [ -z "$user_str" ]; then
+	exit
+fi
+
+# Perform verification if read-only mode is enabled
+check_hestia_demo_mode
+
+#----------------------------------------------------------#
+#                       Action                             #
+#----------------------------------------------------------#
+
+user_shell_rssh_nologin=$(grep "^$user:" /etc/passwd | egrep "rssh|nologin")
+
+# Only remove the jail when it's not needed for rssh or nologin
+if [ -z "$user_shell_rssh_nologin" ]; then
+	# chown permissions back to user:user
+	if [ -d "/home/$user" ]; then
+		chown $user:$user /home/$user
+	fi
+
+	# Deleting chroot jail for SSH
+	delete_chroot_jail $user
+fi
+
+# Deleting user from groups
+gpasswd -d $user ssh-jailed > /dev/null 2>&1
+
+#----------------------------------------------------------#
+#                       Hestia                             #
+#----------------------------------------------------------#
+
+# Disabling user jail
+update_user_value "$user" '$SHELL_JAIL_ENABLED' "no"
+
+# Restart ssh service
+service sshd restart > /dev/null 2>&1
+
+# Logging
+log_event "$OK" "$ARGUMENTS"
+
+exit

+ 28 - 26
bin/v-list-user

@@ -44,6 +44,7 @@ json_list() {
         "HOME": "'$HOME'",
         "NS": "'$NS'",
         "SHELL": "'$SHELL'",
+        "SHELL_JAIL_ENABLED": "'$SHELL_JAIL_ENABLED'",
         "BACKUPS": "'$BACKUPS'",
         "CONTACT": "'$CONTACT'",
         "CRON_REPORTS": "'$CRON_REPORTS'",
@@ -94,28 +95,29 @@ json_list() {
 
 # SHELL list function
 shell_list() {
-	echo "USERNAME:       $USER"
-	echo "FULL NAME:      $NAME"
-	echo "EMAIL:          $CONTACT"
-	echo "LANGUAGE:       $LANGUAGE"
-	echo "THEME:          $THEME"
-	echo "SUSPENDED:      $SUSPENDED"
-	echo "PACKAGE:        $PACKAGE"
-	echo "SHELL:          $SHELL"
-	echo "WEB DOMAINS:    $U_WEB_DOMAINS/$WEB_DOMAINS"
-	echo "WEB ALIASES:    $U_WEB_ALIASES/$WEB_ALIASES"
-	echo "DNS DOMAINS:    $U_DNS_DOMAINS/$DNS_DOMAINS"
-	echo "DNS RECORDS:    $U_DNS_RECORDS/$DNS_RECORDS"
-	echo "MAIL DOMAINS:   $U_MAIL_DOMAINS/$MAIL_DOMAINS"
-	echo "MAIL ACCOUNTS:  $U_MAIL_ACCOUNTS/$MAIL_ACCOUNTS"
-	echo "BACKUPS:        $U_BACKUPS/$BACKUPS"
-	echo "DATABASES:      $U_DATABASES/$DATABASES"
-	echo "CRON_JOBS:      $U_CRON_JOBS/$CRON_JOBS"
-	echo "DISK:           $U_DISK/$DISK_QUOTA"
-	echo "BANDWIDTH:      $U_BANDWIDTH/$BANDWIDTH"
-	echo "IP ADDRESSES    $IP_AVAIL/$IP_OWNED"
-	echo "TIME:           $TIME"
-	echo "DATE:           $DATE"
+	echo "USERNAME:           $USER"
+	echo "FULL NAME:          $NAME"
+	echo "EMAIL:              $CONTACT"
+	echo "LANGUAGE:           $LANGUAGE"
+	echo "THEME:              $THEME"
+	echo "SUSPENDED:          $SUSPENDED"
+	echo "PACKAGE:            $PACKAGE"
+	echo "SHELL:              $SHELL"
+	echo "SHELL_JAIL_ENABLED: $SHELL_JAIL_ENABLED"
+	echo "WEB DOMAINS:        $U_WEB_DOMAINS/$WEB_DOMAINS"
+	echo "WEB ALIASES:        $U_WEB_ALIASES/$WEB_ALIASES"
+	echo "DNS DOMAINS:        $U_DNS_DOMAINS/$DNS_DOMAINS"
+	echo "DNS RECORDS:        $U_DNS_RECORDS/$DNS_RECORDS"
+	echo "MAIL DOMAINS:       $U_MAIL_DOMAINS/$MAIL_DOMAINS"
+	echo "MAIL ACCOUNTS:      $U_MAIL_ACCOUNTS/$MAIL_ACCOUNTS"
+	echo "BACKUPS:            $U_BACKUPS/$BACKUPS"
+	echo "DATABASES:          $U_DATABASES/$DATABASES"
+	echo "CRON_JOBS:          $U_CRON_JOBS/$CRON_JOBS"
+	echo "DISK:               $U_DISK/$DISK_QUOTA"
+	echo "BANDWIDTH:          $U_BANDWIDTH/$BANDWIDTH"
+	echo "IP ADDRESSES        $IP_AVAIL/$IP_OWNED"
+	echo "TIME:               $TIME"
+	echo "DATE:               $DATE"
 }
 
 # PLAIN list function
@@ -124,8 +126,8 @@ plain_list() {
 	echo -ne "$BACKEND_TEMPLATE\t$PROXY_TEMPLATE\t$DNS_TEMPLATE\t"
 	echo -ne "$WEB_DOMAINS\t$WEB_ALIASES\t$DNS_DOMAINS\t$DNS_RECORDS\t"
 	echo -ne "$MAIL_DOMAINS\t$MAIL_ACCOUNTS\t$DATABASES\t$CRON_JOBS\t"
-	echo -ne "$DISK_QUOTA\t$BANDWIDTH\t$NS\t$HOME\t$SHELL\t$BACKUPS\t"
-	echo -ne "$CONTACT\t$CRON_REPORTS\t$RKEY\t$SUSPENDED\t"
+	echo -ne "$DISK_QUOTA\t$BANDWIDTH\t$NS\t$HOME\t$SHELL\t$SHELL_JAIL_ENABLED\t"
+	echo -ne "$BACKUPS\t$CONTACT\t$CRON_REPORTS\t$RKEY\t$SUSPENDED\t"
 	echo -ne "$SUSPENDED_USERS\t$SUSPENDED_WEB\t$SUSPENDED_DNS\t"
 	echo -ne "$SUSPENDED_MAIL\t$SUSPENDED_DB\t$SUSPENDED_CRON\t"
 	echo -ne "$IP_AVAIL\t$IP_OWNED\t$U_USERS\t$U_DISK\t$U_DISK_DIRS\t"
@@ -153,8 +155,8 @@ csv_list() {
 	echo -n "$BACKEND_TEMPLATE,$PROXY_TEMPLATE,$DNS_TEMPLATE,$WEB_DOMAINS,"
 	echo -n "$WEB_ALIASES,$DNS_DOMAINS,$DNS_RECORDS,$MAIL_DOMAINS,"
 	echo -n "$MAIL_ACCOUNTS,$DATABASES,$CRON_JOBS,$DISK_QUOTA,$BANDWIDTH,"
-	echo -n "\"$NS\",$HOME,$SHELL,$BACKUPS,$CONTACT,$CRON_REPORTS,\"$RKEY\","
-	echo -n "$SUSPENDED,$SUSPENDED_USERS,$SUSPENDED_WEB,$SUSPENDED_DNS,"
+	echo -n "\"$NS\",$HOME,$SHELL,$SHELL_JAIL_ENABLED,$BACKUPS,$CONTACT,$CRON_REPORTS,"
+	echo -n "\"$RKEY\",$SUSPENDED,$SUSPENDED_USERS,$SUSPENDED_WEB,$SUSPENDED_DNS,"
 	echo -n "$SUSPENDED_MAIL,$SUSPENDED_DB,$SUSPENDED_CRON,$IP_AVAIL,"
 	echo -n "$IP_OWNED,$U_USERS,$U_DISK,$U_DISK_DIRS,$U_DISK_WEB,"
 	echo -n "$U_DISK_MAIL,$U_DISK_DB,$U_BANDWIDTH,$U_WEB_DOMAINS,$U_WEB_SSL,"

+ 25 - 23
bin/v-list-user-package

@@ -41,6 +41,7 @@ json_list() {
         "BANDWIDTH": "'$BANDWIDTH'",
         "NS": "'$NS'",
         "SHELL": "'$SHELL'",
+        "SHELL_JAIL_ENABLED": "'$SHELL_JAIL_ENABLED'",
         "BACKUPS": "'$BACKUPS'",
         "TIME": "'$TIME'",
         "DATE": "'$DATE'"
@@ -50,27 +51,28 @@ json_list() {
 
 # SHELL list function
 shell_list() {
-	echo "PACKAGE:          $PACKAGE"
-	echo "WEB TEMPLATE:     $WEB_TEMPLATE"
-	echo "BACKEND TEMPLATE: $BACKEND_TEMPLATE"
-	echo "PROXY TEMPLATE:   $PROXY_TEMPLATE"
-	echo "DNS TEMPLATE:     $DNS_TEMPLATE"
-	echo "WEB DOMAINS:      $WEB_DOMAINS"
-	echo "WEB ALIASES:      $WEB_ALIASES"
-	echo "DNS DOMAINS:      $DNS_DOMAINS"
-	echo "DNS RECORDS:      $DNS_RECORDS"
-	echo "MAIL DOMAINS:     $MAIL_DOMAINS"
-	echo "MAIL ACCOUNTS:    $MAIL_ACCOUNTS"
-	echo "RATE_LIMIT:       $RATE_LIMIT"
-	echo "DATABASES:        $DATABASES"
-	echo "CRON JOBS:        $CRON_JOBS"
-	echo "DISK QUOTA:       $DISK_QUOTA"
-	echo "BANDWIDTH:        $BANDWIDTH"
-	echo "NS:               $NS"
-	echo "SHELL:            $SHELL"
-	echo "BACKUPS:          $BACKUPS"
-	echo "TIME:             $TIME"
-	echo "DATE:             $DATE"
+	echo "PACKAGE:          	$PACKAGE"
+	echo "WEB TEMPLATE:     	$WEB_TEMPLATE"
+	echo "BACKEND TEMPLATE: 	$BACKEND_TEMPLATE"
+	echo "PROXY TEMPLATE:   	$PROXY_TEMPLATE"
+	echo "DNS TEMPLATE:     	$DNS_TEMPLATE"
+	echo "WEB DOMAINS:      	$WEB_DOMAINS"
+	echo "WEB ALIASES:      	$WEB_ALIASES"
+	echo "DNS DOMAINS:      	$DNS_DOMAINS"
+	echo "DNS RECORDS:      	$DNS_RECORDS"
+	echo "MAIL DOMAINS:     	$MAIL_DOMAINS"
+	echo "MAIL ACCOUNTS:    	$MAIL_ACCOUNTS"
+	echo "RATE_LIMIT:       	$RATE_LIMIT"
+	echo "DATABASES:        	$DATABASES"
+	echo "CRON JOBS:        	$CRON_JOBS"
+	echo "DISK QUOTA:       	$DISK_QUOTA"
+	echo "BANDWIDTH:        	$BANDWIDTH"
+	echo "NS:               	$NS"
+	echo "SHELL:            	$SHELL"
+	echo "SHELL_JAIL_ENABLED:	$SHELL_JAIL_ENABLED"
+	echo "BACKUPS:          	$BACKUPS"
+	echo "TIME:             	$TIME"
+	echo "DATE:             	$DATE"
 }
 
 # PLAIN list function
@@ -78,7 +80,7 @@ plain_list() {
 	echo -ne "$PACKAGE\t$WEB_TEMPLATE\t$BACKEND_TEMPLATE\t$PROXY_TEMPLATE\t$DNS_TEMPLATE\t"
 	echo -ne "$WEB_DOMAINS\t$WEB_ALIASES\t$DNS_DOMAINS\t$DNS_RECORDS\t"
 	echo -ne "$MAIL_DOMAINS\t$MAIL_ACCOUNTS\t$RATE_LIMIT\t$DATABASES\t$CRON_JOBS\t"
-	echo -e "$DISK_QUOTA\t$BANDWIDTH\t$NS\t$SHELL\t$BACKUPS\t$TIME\t$DATE"
+	echo -e "$DISK_QUOTA\t$BANDWIDTH\t$NS\t$SHELL\t$SHELL_JAIL_ENABLED\t$BACKUPS\t$TIME\t$DATE"
 }
 
 # CSV list function
@@ -86,7 +88,7 @@ csv_list() {
 	echo -n "PACKAGE,WEB_TEMPLATE,BACKEND_TEMPLATE,PROXY_TEMPLATE,DNS_TEMPLATE,"
 	echo -n "WEB_DOMAINS,WEB_ALIASES,DNS_DOMAINS,DNS_RECORDS,"
 	echo -n "MAIL_DOMAINS,MAIL_ACCOUNTS,RATE_LIMIT,DATABASES,CRON_JOBS,"
-	echo "DISK_QUOTA,BANDWIDTH,NS,SHELL,BACKUPS,TIME,DATE"
+	echo "DISK_QUOTA,BANDWIDTH,NS,SHELL,SHELL_JAIL_ENABLED,BACKUPS,TIME,DATE"
 	echo -n "$PACKAGE,$WEB_TEMPLATE,$BACKEND_TEMPLATE,$PROXY_TEMPLATE,$DNS_TEMPLATE,"
 	echo -n "$WEB_DOMAINS,$WEB_ALIASES,$DNS_DOMAINS,$DNS_RECORDS,"
 	echo -n "$MAIL_DOMAINS,$MAIL_ACCOUNTS,$RATE_LIMIT,$DATABASES,$CRON_JOBS,"

+ 22 - 3
func/main.sh

@@ -1263,6 +1263,7 @@ is_format_valid() {
 				soa) is_domain_format_valid "$arg" 'SOA' ;;
 				#missing command: is_format_valid_shell
 				shell) is_format_valid_shell "$arg" ;;
+				shell_jail_enabled) is_boolean_format_valid "$arg" 'shell_jail_enabled' ;;
 				ssl_dir) is_folder_exists "$arg" "$arg_name" ;;
 				stats_pass) is_password_format_valid "$arg" ;;
 				stats_user) is_user_format_valid "$arg" "$arg_name" ;;
@@ -1744,8 +1745,18 @@ add_chroot_jail() {
 		chown 0:0 /srv/jail/$user/home
 		chmod 755 /srv/jail/$user/home
 	fi
+	if [ ! -d /srv/jail/$user/home/$user ]; then
+		mkdir -p /srv/jail/$user/home/$user
+		chown 0:0 /srv/jail/$user/home/$user
+		chmod 755 /srv/jail/$user/home/$user
+	fi
+	if [ ! -d /srv/jail/$user/tmp ]; then
+		mkdir -p /srv/jail/$user/tmp
+		chown "$user:$user" /srv/jail/$user/tmp
+		chmod 755 /srv/jail/$user/tmp
+	fi
 
-	systemd=$(systemd-escape -p --suffix=mount "/srv/jail/$user/home")
+	systemd=$(systemd-escape -p --suffix=mount "/srv/jail/$user/home/$user")
 	cat > "/etc/systemd/system/$systemd" << EOF
 [Unit]
 Description=Mount $user's home directory to the jail chroot
@@ -1753,7 +1764,7 @@ Before=local-fs.target
 
 [Mount]
 What=$(getent passwd $user | cut -d : -f 6)
-Where=/srv/jail/$user/home
+Where=/srv/jail/$user/home/$user
 Type=none
 Options=bind
 LazyUnmount=yes
@@ -1770,11 +1781,19 @@ EOF
 delete_chroot_jail() {
 	local user=$1
 
+	# Backwards compatibility with old style home jail
 	systemd=$(systemd-escape -p --suffix=mount "/srv/jail/$user/home")
 	systemctl stop "$systemd" > /dev/null 2>&1
 	systemctl disable "$systemd" > /dev/null 2>&1
 	rm -f "/etc/systemd/system/$systemd"
+
+	# Remove the new style home jail
+	systemd=$(systemd-escape -p --suffix=mount "/srv/jail/$user/home/$user")
+	systemctl stop "$systemd" > /dev/null 2>&1
+	systemctl disable "$systemd" > /dev/null 2>&1
+	rm -f "/etc/systemd/system/$systemd"
+
 	systemctl daemon-reload > /dev/null 2>&1
-	rmdir /srv/jail/$user/home > /dev/null 2>&1
+	rm -r /srv/jail/$user/ > /dev/null 2>&1
 	rmdir /srv/jail/$user > /dev/null 2>&1
 }

+ 5 - 0
func/rebuild.sh

@@ -62,6 +62,9 @@ rebuild_user_conf() {
 	if [ -z "${RATE_LIMIT+x}" ]; then
 		sed -i "/MAIL_ACCOUNTS/a RATE_LIMIT='200'" $USER_DATA/user.conf
 	fi
+	if [ -z "${SHELL_JAIL_ENABLED+x}" ]; then
+		sed -i "/SHELL/a SHELL_JAIL_ENABLED='no'" $USER_DATA/user.conf
+	fi
 	# Run template trigger
 	if [ -x "$HESTIA/data/packages/$PACKAGE.sh" ]; then
 		$HESTIA/data/packages/$PACKAGE.sh "$user" "$CONTACT" "$NAME"
@@ -124,6 +127,8 @@ rebuild_user_conf() {
 
 	$BIN/v-add-user-sftp-jail "$user"
 
+	$BIN/v-add-user-ssh-jail "$user"
+
 	# Update disk pipe
 	sed -i "/ $user$/d" $HESTIA/data/queue/disk.pipe
 	echo "$BIN/v-update-user-disk $user" >> $HESTIA/data/queue/disk.pipe

+ 226 - 0
install/common/jailkit/jk_init.ini

@@ -0,0 +1,226 @@
+[uidbasics]
+# this section probably needs adjustment on 64bit systems
+# or non-Linux systems
+comment = common files for all jails that need user/group information
+paths = /lib/libnsl.so.*, /lib64/libnsl.so.*, /lib/libnss*.so.2, /lib64/libnss*.so.2, /lib/i386-linux-gnu/libnsl.so.*, /lib/i386-linux-gnu/libnss*.so.2, /lib/x86_64-linux-gnu/libnsl.so.*, /lib/x86_64-linux-gnu/libnss*.so.2, /lib/arm-linux-gnueabihf/libnss*.so.2, /lib/arm-linux-gnueabihf/libnsl*.so.*, /etc/nsswitch.conf, /etc/ld.so.conf
+# Solaris needs
+# paths = /etc/default/nss, /lib/libnsl.so.1, /usr/lib/nss_*.so.1, /etc/nsswitch.conf
+
+[netbasics]
+comment = common files for all jails that need any internet connectivity
+paths = /lib/libnss_dns.so.2, /lib64/libnss_dns.so.2, /lib/libnss_mdns*.so.2, /etc/resolv.conf, /etc/host.conf, /etc/hosts, /etc/protocols, /etc/services, /etc/ssl/certs/, /usr/lib/ssl/certs
+# on Solaris devices /dev/udp and /dev/tcp might be needed too, not sure
+
+[logbasics]
+comment = timezone information and log sockets
+paths = /etc/localtime
+need_logsocket = 1
+# Solaris does not need logsocket
+# but needs
+# devices = /dev/log, /dev/conslog
+
+[jk_lsh]
+comment = Jailkit limited shell
+paths = /usr/sbin/jk_lsh, /etc/jailkit/jk_lsh.ini
+users = root
+groups = root
+includesections = uidbasics, logbasics
+
+[limitedshell]
+comment = alias for jk_lsh
+includesections = jk_lsh
+
+[cvs]
+comment = Concurrent Versions System
+paths = cvs
+devices = /dev/null
+
+[git]
+comment = Fast Version Control System
+paths = /usr/bin/git*, /usr/lib/git-core, /usr/bin/basename, /bin/uname, /usr/bin/pager
+includesections = editors, perl
+
+[scp]
+comment = ssh secure copy
+paths = scp
+includesections = netbasics, uidbasics
+devices = /dev/urandom, /dev/null
+
+[sftp]
+comment = ssh secure ftp
+paths = /usr/lib/sftp-server, /usr/libexec/openssh/sftp-server, /usr/lib/misc/sftp-server, /usr/libexec/sftp-server, /usr/lib/openssh/sftp-server
+includesections = netbasics, uidbasics
+devices = /dev/urandom, /dev/null
+# on solaris
+#paths = /usr/lib/ssh/sftp-server
+
+[ssh]
+comment = ssh secure shell
+paths = ssh
+includesections = netbasics, uidbasics
+devices = /dev/urandom, /dev/tty, /dev/null
+
+[rsync]
+paths = rsync
+includesections = netbasics, uidbasics
+
+[procmail]
+comment = procmail mail delivery
+paths = procmail, /bin/sh
+devices = /dev/null
+
+[basicshell]
+comment = bash based shell with several basic utilities
+paths = /bin/sh, bash, ls, cat, chmod, mkdir, cp, cpio, date, dd, echo, egrep, false, fgrep, grep, groups, gunzip, gzip, ln, ls, mkdir, mktemp, more, mv, pwd, rm, rmdir, sed, sh, sleep, sync, tar, touch, true, uncompress, unzip, zcat, zip, zstd, /etc/motd, /etc/issue, /etc/bash.bashrc, /etc/bashrc, /etc/profile, /usr/lib/locale/en_US.utf8
+users = root
+groups = root
+includesections = uidbasics
+
+[interactiveshell]
+comment = for ssh access to a full shell
+includesections = uidbasics, basicshell, terminfo, editors, extendedshell
+
+[midnightcommander]
+comment = Midnight Commander
+paths = mc, mcedit, mcview, /usr/share/mc
+includesections = basicshell, terminfo
+
+[extendedshell]
+comment = bash shell including things like awk, bzip, tail, less
+paths = awk, bzip2, bunzip2, ldd, less, clear, cut, du, find, head, less, md5sum, nice, sort, tac, tail, tr, sort, wc, watch, whoami
+includesections = basicshell, midnightcommander, editors
+
+[terminfo]
+comment = terminfo databases, required for example for ncurses or vim
+paths = /etc/terminfo, /usr/share/terminfo, /lib/terminfo
+
+[editors]
+comment = vim, joe and nano
+includesections = terminfo
+paths = joe, nano, vi, vim, /etc/vimrc, /etc/joe, /usr/share/vim
+
+[netutils]
+comment = several internet utilities like wget, ftp, rsync, scp, ssh
+paths = wget, lynx, ftp, host, rsync, smbclient
+includesections = netbasics, ssh, sftp, scp
+
+[apacheutils]
+comment = htpasswd utility
+paths = htpasswd
+
+[extshellplusnet]
+comment = alias for extendedshell + netutils + apacheutils
+includesections = extendedshell, netutils, apacheutils
+
+[openvpn]
+comment = jail for the openvpn daemon
+paths = /usr/sbin/openvpn
+users = root,nobody
+groups = root,nogroup
+#includesections = netbasics
+devices = /dev/urandom, /dev/random, /dev/net/tun
+includesections = netbasics, uidbasics
+need_logsocket = 1
+
+[apache]
+comment = the apache webserver, very basic setup, probably too limited for you
+paths = /usr/sbin/apache
+users = root, www-data
+groups = root, www-data
+includesections = netbasics, uidbasics
+
+[perl]
+comment = the perl interpreter and libraries
+paths = perl, /usr/lib/perl, /usr/lib/perl5, /usr/share/perl, /usr/share/perl5
+
+[xauth]
+comment = getting X authentication to work
+paths = /usr/bin/X11/xauth, /usr/X11R6/lib/X11/rgb.txt, /etc/ld.so.conf
+
+[xclients]
+comment = minimal files for X clients
+paths = /usr/X11R6/lib/X11/rgb.txt
+includesections = xauth
+
+[vncserver]
+comment = the VNC server program
+paths = Xvnc, Xrealvnc, /usr/X11R6/lib/X11/fonts/
+includesections = xclients
+
+[ping]
+comment = Ping program
+paths_w_setuid = /bin/ping
+
+#[xterm]
+#comment = xterm
+#paths = /usr/bin/X11/xterm, /usr/share/terminfo, /etc/terminfo
+#devices = /dev/pts/0, /dev/pts/1, /dev/pts/2, /dev/pts/3, /dev/pts/4, /dev/ptyb4, /dev/ptya4, /dev/tty, /dev/tty0, /dev/tty4
+
+# Hestia Jail Settings
+[node]
+comment = NodeJS
+paths = npm, npx, node, nodejs, /usr/lib/nodejs, /usr/share/nodejs, /usr/share/npm, /usr/lib/node_modules, /usr/local/lib/nodejs, /usr/local/lib/node_modules
+
+[env]
+comment = environment variables
+executables = /usr/bin/env
+
+# The default version Hestia installs is 8.2 use this as the common PHP version
+[php]
+comment = default php version and libraries
+paths = /usr/bin/php
+includesections = php_common, php8.2
+
+[php_common]
+comment = common php directories and libraries
+# Careful!
+# Make sure not to add the upper /etc/php/ or /etc/php/fpm directories.
+# theses are unneeded in the jails and adding them can potentially leak
+# custom server settings to the customers.
+paths = /usr/bin/php, /usr/lib/php/, /usr/share/php/, /usr/share/zoneinfo/
+includesections = env, logbasics, netbasics
+
+[php5_6]
+comment = php version 5.6
+paths = /usr/bin/php5.6, /usr/lib/php/5.6/, /usr/lib/php/20131226/, /usr/share/php/5.6/, /etc/php/5.6/cli/, /etc/php/5.6/mods-available/
+includesections = php_common
+
+[php7_0]
+comment = php version 7.0
+paths = /usr/bin/php7.0, /usr/lib/php/7.0/, /usr/lib/php/20151012/, /usr/share/php/7.0/, /etc/php/7.0/cli/, /etc/php/7.0/mods-available/
+includesections = php_common
+
+[php7_1]
+comment = php version 7.1
+paths = /usr/bin/php7.1, /usr/lib/php/7.1/, /usr/lib/php/20160303/, /usr/share/php/7.1/, /etc/php/7.1/cli/, /etc/php/7.1/mods-available/
+includesections = php_common
+
+[php7_2]
+comment = php version 7.2
+paths = /usr/bin/php7.2, /usr/lib/php/7.2/, /usr/lib/php/20170718/, /usr/share/php/7.2/, /etc/php/7.2/cli/, /etc/php/7.2/mods-available/
+includesections = php_common
+
+[php7_3]
+comment = php version 7.3
+paths = /usr/bin/php7.3, /usr/lib/php/7.3/, /usr/lib/php/20180731/, /usr/share/php/7.3/, /etc/php/7.3/cli/, /etc/php/7.3/mods-available/
+includesections = php_common
+
+[php7_4]
+comment = php version 7.4
+paths = /usr/bin/php7.4, /usr/lib/php/7.4/, /usr/lib/php/20190902/, /usr/share/php/7.4/, /etc/php/7.4/cli/, /etc/php/7.4/mods-available/
+includesections = php_common
+
+[php8_0]
+comment = php version 8.0
+paths = /usr/bin/php8.0, /usr/lib/php/8.0/, /usr/lib/php/20200930/, /usr/share/php/8.0/, /etc/php/8.0/cli/, /etc/php/8.0/mods-available/
+includesections = php_common
+
+[php8_1]
+comment = php version 8.1
+paths = /usr/bin/php8.1, /usr/lib/php/8.1/, /usr/lib/php/20210902/, /usr/share/php/8.1/, /etc/php/8.1/cli/, /etc/php/8.1/mods-available/
+includesections = php_common
+
+[php8_2]
+comment = php version 8.2
+paths = /usr/bin/php8.2, /usr/lib/php/8.2/, /usr/lib/php/20220829/, /usr/share/php/8.2/, /etc/php/8.2/cli/, /etc/php/8.2/mods-available/
+includesections = php_common

+ 1 - 0
install/common/packages/default.pkg

@@ -15,6 +15,7 @@ DISK_QUOTA='unlimited'
 BANDWIDTH='unlimited'
 NS='ns1.domain.tld,ns2.domain.tld'
 SHELL='nologin'
+SHELL_JAIL_ENABLED='no'
 BACKUPS='1'
 TIME='18:00:00'
 DATE='2019-01-15'

+ 1 - 0
install/common/packages/system.pkg

@@ -15,6 +15,7 @@ DISK_QUOTA='unlimited'
 BANDWIDTH='unlimited'
 NS='ns1.domain.tld,ns2.domain.tld'
 SHELL='nologin'
+SHELL_JAIL_ENABLED='no'
 BACKUPS='1'
 TIME='00:00:00'
 DATE='2022-01-20'

+ 7 - 2
install/hst-install-debian.sh

@@ -50,7 +50,7 @@ software="acl apache2 apache2-suexec-custom apache2-suexec-pristine apache2-util
   php$fpm_v php$fpm_v-apcu php$fpm_v-bz2 php$fpm_v-cgi php$fpm_v-cli php$fpm_v-common php$fpm_v-curl php$fpm_v-gd
   php$fpm_v-imagick php$fpm_v-imap php$fpm_v-intl php$fpm_v-ldap php$fpm_v-mbstring php$fpm_v-mysql php$fpm_v-opcache
   php$fpm_v-pgsql php$fpm_v-pspell php$fpm_v-readline php$fpm_v-xml php$fpm_v-zip postgresql postgresql-contrib
-  proftpd-basic quota rrdtool rsyslog spamd sudo sysstat unrar-free unzip util-linux vim-common vsftpd xxd whois zip zstd"
+  proftpd-basic quota rrdtool rsyslog spamd sudo sysstat unrar-free unzip util-linux vim-common vsftpd xxd whois zip zstd jailkit"
 
 installer_dependencies="apt-transport-https ca-certificates curl dirmngr gnupg openssl wget"
 
@@ -1494,11 +1494,16 @@ echo "[ * ] Enabling SFTP jail..."
 $HESTIA/bin/v-add-sys-sftp-jail > /dev/null 2>&1
 check_result $? "can't enable sftp jail"
 
+# Enable ssh jail
+echo "[ * ] Enabling SSH jail..."
+$HESTIA/bin/v-add-sys-ssh-jail > /dev/null 2>&1
+check_result $? "can't enable ssh jail"
+
 # Adding Hestia admin account
 echo "[ * ] Creating default admin account..."
 $HESTIA/bin/v-add-user "$username" "$vpass" "$email" "default" "System Administrator"
 check_result $? "can't create admin user"
-$HESTIA/bin/v-change-user-shell "$username" nologin
+$HESTIA/bin/v-change-user-shell "$username" nologin no
 $HESTIA/bin/v-change-user-role "$username" admin
 $HESTIA/bin/v-change-user-language "$username" "$lang"
 $HESTIA/bin/v-change-sys-config-value 'POLICY_SYSTEM_PROTECTED_ADMIN' 'yes'

+ 7 - 2
install/hst-install-ubuntu.sh

@@ -51,7 +51,7 @@ software="acl apache2 apache2.2-common apache2-suexec-custom apache2-utils appar
   php$fpm_v php$fpm_v-apcu php$fpm_v-bz2 php$fpm_v-cgi php$fpm_v-cli php$fpm_v-common php$fpm_v-curl php$fpm_v-gd
   php$fpm_v-imagick php$fpm_v-imap php$fpm_v-intl php$fpm_v-ldap php$fpm_v-mbstring php$fpm_v-mysql php$fpm_v-opcache
   php$fpm_v-pgsql php$fpm_v-pspell php$fpm_v-readline php$fpm_v-xml php$fpm_v-zip postgresql postgresql-contrib
-  proftpd-basic quota rrdtool rsyslog setpriv spamassassin sudo sysstat unzip vim-common vsftpd whois zip zstd"
+  proftpd-basic quota rrdtool rsyslog setpriv spamassassin sudo sysstat unzip vim-common vsftpd whois zip zstd jailkit"
 
 installer_dependencies="apt-transport-https ca-certificates curl dirmngr gnupg openssl software-properties-common wget"
 
@@ -1509,11 +1509,16 @@ echo "[ * ] Enabling SFTP jail..."
 $HESTIA/bin/v-add-sys-sftp-jail > /dev/null 2>&1
 check_result $? "can't enable sftp jail"
 
+# Enable ssh jail
+echo "[ * ] Enabling SSH jail..."
+$HESTIA/bin/v-add-sys-ssh-jail > /dev/null 2>&1
+check_result $? "can't enable ssh jail"
+
 # Adding Hestia admin account
 echo "[ * ] Creating default admin account..."
 $HESTIA/bin/v-add-user $username $vpass $email "default" "System Administrator"
 check_result $? "can't create admin user"
-$HESTIA/bin/v-change-user-shell $username nologin
+$HESTIA/bin/v-change-user-shell $username nologin no
 $HESTIA/bin/v-change-user-role $username admin
 $HESTIA/bin/v-change-user-language $username $lang
 $HESTIA/bin/v-change-sys-config-value 'POLICY_SYSTEM_PROTECTED_ADMIN' 'yes'

+ 7 - 0
install/upgrade/versions/1.9.0.sh

@@ -73,5 +73,12 @@ fi
 
 chown hestiaweb:hestiaweb /usr/local/hestia/data/sessions
 
+packages=$(ls --sort=time $HESTIA/data/packages | grep .pkg)
+for package in $packages; do
+	if [ -z "$(grep -e 'SHELL_JAIL_ENABLED' $HESTIA/data/packages/$package)" ]; then
+		echo "SHELL_JAIL_ENABLED='no'" >> $HESTIA/data/packages/$package
+	fi
+done
+
 $BIN/v-add-user-notification 'admin' 'Hestia securirty has been upgraded' 'Here should come a nice message about the upgrade and how to change the user name of the admin user!'
 add_upgrade_message 'Here should come a nice message about the upgrade and how to change the user name of the admin user!'

+ 1 - 1
src/deb/hestia/control

@@ -6,7 +6,7 @@ Section: admin
 Maintainer: HestiaCP <info@hestiacp.com>
 Homepage: https://www.hestiacp.com
 Architecture: amd64
-Depends: bash, awk, sed, acl, sysstat, setpriv | util-linux (>= 2.33), zstd, lsb-release, idn2, jq
+Depends: bash, awk, sed, acl, sysstat, setpriv | util-linux (>= 2.33), zstd, lsb-release, idn2, jq, jailkit
 Description: hestia
  hestia is an open source hosting control panel.
  hestia has a clean and focused interface without the clutter.

+ 1 - 1
src/deb/hestia/preinst

@@ -12,7 +12,7 @@ Source0:        hestia-%{version}.tar.gz
 Source1:        hestia.service
 Vendor:         hestiacp.com
 Requires:       redhat-release >= 8
-Requires:       bash, chkconfig, gawk, sed, acl, sysstat, (setpriv or util-linux), zstd, jq
+Requires:       bash, chkconfig, gawk, sed, acl, sysstat, (setpriv or util-linux), zstd, jq, jailkit
 Conflicts:      vesta
 Provides:       hestia = %{version}
 BuildRequires:  systemd

+ 14 - 4
test/test.bats

@@ -409,7 +409,7 @@ function check_ip_not_banned(){
 }
 
 @test "User: Change user shell" {
-    run v-change-user-shell $user bash
+    run v-change-user-shell $user bash no
     assert_success
     refute_output
 
@@ -420,22 +420,32 @@ function check_ip_not_banned(){
 }
 
 @test "User: Change user invalid shell" {
-    run v-change-user-shell $user bashinvalid
+    run v-change-user-shell $user bashinvalid no
     assert_failure $E_INVALID
     assert_output --partial 'shell bashinvalid is not valid'
 }
 
 @test "User: Change user nologin" {
-    run v-change-user-shell $user nologin
+    run v-change-user-shell $user nologin no
     assert_success
     refute_output
 
     run stat -c '%U' /home/$user
     assert_output --partial 'root'
-		mount_file=$(systemd-escape -p --suffix=mount "/srv/jail/$user/home")
+		mount_file=$(systemd-escape -p --suffix=mount "/srv/jail/$user/home/$user")
 		assert_file_exist /etc/systemd/system/$mount_file
 }
 
+@test "User: Change user bash with jail" {
+    run v-change-user-shell $user bash yes
+    assert_success
+    refute_output
+
+    run stat -c '%U' /home/$user
+    assert_output --partial 'root'
+		mount_file=$(systemd-escape -p --suffix=mount "/srv/jail/$user/home/$user")
+		assert_file_exist /etc/systemd/system/$mount_file
+}
 
 @test "User: Change user default ns" {
     run v-change-user-ns $user ns0.com ns1.com ns2.com ns3.com

+ 15 - 0
web/add/package/index.php

@@ -92,6 +92,14 @@ if (!empty($_POST["ok"])) {
 			$errors[] = _("Nameserver 2");
 		}
 	}
+	if (
+		isset($_POST["v_shell"]) &&
+		isset($_POST["v_shell_jail_enabled"]) &&
+		in_array($_POST["v_shell"], ["nologin", "rssh"]) &&
+		$_POST["v_shell_jail_enabled"] == "yes"
+	) {
+		$_SESSION["error_msg"] = _("Cannot combine nologin and rssh shell with jailed shell.");
+	}
 	if (!empty($errors[0])) {
 		foreach ($errors as $i => $error) {
 			if ($i == 0) {
@@ -109,6 +117,9 @@ if (!empty($_POST["ok"])) {
 		$v_proxy_template = quoteshellarg($_POST["v_proxy_template"]);
 		$v_dns_template = quoteshellarg($_POST["v_dns_template"]);
 		$v_shell = quoteshellarg($_POST["v_shell"]);
+		$v_shell_jail_enabled = quoteshellarg(
+			!empty($_POST["v_shell_jail_enabled"]) ? "yes" : "no",
+		);
 		$v_web_domains = quoteshellarg($_POST["v_web_domains"]);
 		$v_web_aliases = quoteshellarg($_POST["v_web_aliases"]);
 		$v_dns_domains = quoteshellarg($_POST["v_dns_domains"]);
@@ -176,6 +187,7 @@ if (!empty($_POST["ok"])) {
 			$pkg .= "RATE_LIMIT=" . $v_ratelimit . "\n";
 			$pkg .= "NS=" . $v_ns . "\n";
 			$pkg .= "SHELL=" . $v_shell . "\n";
+			$pkg .= "SHELL_JAIL_ENABLED=" . $v_shell_jail_enabled . "\n";
 			$pkg .= "BACKUPS=" . $v_backups . "\n";
 			$pkg .= "TIME=" . $v_time . "\n";
 			$pkg .= "DATE=" . $v_date . "\n";
@@ -258,6 +270,9 @@ if (empty($v_dns_template)) {
 if (empty($v_shell)) {
 	$v_shell = "nologin";
 }
+if (empty($v_shell_jail_enabled)) {
+	$v_shell_jail_enabled = "no";
+}
 if (empty($v_web_domains)) {
 	$v_web_domains = "'1'";
 }

+ 16 - 0
web/edit/package/index.php

@@ -51,6 +51,7 @@ $v_cron_jobs = $data[$v_package]["CRON_JOBS"];
 $v_disk_quota = $data[$v_package]["DISK_QUOTA"];
 $v_bandwidth = $data[$v_package]["BANDWIDTH"];
 $v_shell = $data[$v_package]["SHELL"];
+$v_shell_jail_enabled = $data[$v_package]["SHELL_JAIL_ENABLED"];
 $v_ns = $data[$v_package]["NS"];
 $nameservers = explode(",", $v_ns);
 if (empty($nameservers[0])) {
@@ -202,6 +203,15 @@ if (!empty($_POST["save"])) {
 		}
 	}
 
+	if (
+		isset($_POST["v_shell"]) &&
+		isset($_POST["v_shell_jail_enabled"]) &&
+		in_array($_POST["v_shell"], ["nologin", "rssh"]) &&
+		$_POST["v_shell_jail_enabled"] == "yes"
+	) {
+		$_SESSION["error_msg"] = _("Cannot combine nologin and rssh shell with jailed shell.");
+	}
+
 	if (!empty($errors[0])) {
 		foreach ($errors as $i => $error) {
 			if ($i == 0) {
@@ -229,6 +239,11 @@ if (!empty($_POST["save"])) {
 	} else {
 		$v_shell = "nologin";
 	}
+	if (!empty($_POST["v_shell_jail_enabled"])) {
+		$v_shell_jail_enabled = quoteshellarg($_POST["v_shell_jail_enabled"]);
+	} else {
+		$v_shell_jail_enabled = "no";
+	}
 	$v_web_domains = quoteshellarg($_POST["v_web_domains"]);
 	$v_web_aliases = quoteshellarg($_POST["v_web_aliases"]);
 	$v_dns_domains = quoteshellarg($_POST["v_dns_domains"]);
@@ -290,6 +305,7 @@ if (!empty($_POST["save"])) {
 	$pkg .= "BANDWIDTH=" . $v_bandwidth . "\n";
 	$pkg .= "NS=" . $v_ns . "\n";
 	$pkg .= "SHELL=" . $v_shell . "\n";
+	$pkg .= "SHELL_JAIL_ENABLED=" . $v_shell_jail_enabled . "\n";
 	$pkg .= "BACKUPS=" . $v_backups . "\n";
 	$pkg .= "TIME=" . $v_time . "\n";
 	$pkg .= "DATE=" . $v_date . "\n";

+ 21 - 2
web/edit/user/index.php

@@ -55,6 +55,7 @@ $v_user_theme = $data[$v_username]["THEME"];
 $v_sort_order = $data[$v_username]["PREF_UI_SORT"];
 $v_name = $data[$v_username]["NAME"];
 $v_shell = $data[$v_username]["SHELL"];
+$v_shell_jail_enabled = $data[$v_username]["SHELL_JAIL_ENABLED"];
 $v_twofa = $data[$v_username]["TWOFA"];
 $v_qrcode = $data[$v_username]["QRCODE"];
 $v_phpcli = $data[$v_username]["PHPCLI"];
@@ -367,18 +368,36 @@ if (!empty($_POST["save"])) {
 		}
 		// Change shell (admin only)
 		if (!empty($_POST["v_shell"])) {
+			if (empty($_POST["v_shell_jail_enabled"])) {
+				$_POST["v_shell_jail_enabled"] = "no";
+			}
+
 			if (
-				$v_shell != $_POST["v_shell"] &&
+				in_array($_POST["v_shell"], ["nologin", "rssh"]) &&
+				$_POST["v_shell_jail_enabled"] == "yes"
+			) {
+				$_SESSION["error_msg"] = _(
+					"Cannot combine nologin and rssh shell with jailed shell.",
+				);
+			}
+
+			if (
+				($v_shell != $_POST["v_shell"] ||
+					$v_shell_jail_enabled != $_POST["v_shell_jail_enabled"]) &&
 				$_SESSION["userContext"] === "admin" &&
 				empty($_SESSION["error_msg"])
 			) {
 				$v_shell = quoteshellarg($_POST["v_shell"]);
+				$v_shell_jail_enabled = quoteshellarg($_POST["v_shell_jail_enabled"]);
+
 				exec(
 					HESTIA_CMD .
 						"v-change-user-shell " .
 						quoteshellarg($v_username) .
 						" " .
-						$v_shell,
+						$v_shell .
+						" " .
+						$v_shell_jail_enabled,
 					$output,
 					$return_var,
 				);

+ 6 - 0
web/templates/pages/add_package.php

@@ -270,6 +270,12 @@
 							<?php endforeach; ?>
 						</select>
 					</div>
+					<div class="form-check u-mb10">
+						<input class="form-check-input" type="checkbox" name="v_shell_jail_enabled" id="v_shell_jail_enabled" value="yes" <?php if (htmlentities(trim($v_shell_jail_enabled, "'")) == "yes") echo 'checked' ?>>
+						<label for="v_shell_jail_enabled">
+							<?= _("Jail User Shell") ?>
+						</label>
+					</div>
 				</div>
 			</details>
 		</div>

+ 6 - 0
web/templates/pages/edit_package.php

@@ -275,6 +275,12 @@
 							<?php endforeach; ?>
 						</select>
 					</div>
+					<div class="form-check u-mb10">
+						<input class="form-check-input" type="checkbox" name="v_shell_jail_enabled" id="v_shell_jail_enabled" value="yes" <?php if (htmlentities(trim($v_shell_jail_enabled, "'")) == "yes") echo 'checked' ?>>
+						<label for="v_shell_jail_enabled">
+							<?= _("Jail User Shell") ?>
+						</label>
+					</div>
 				</div>
 			</details>
 		</div>

+ 6 - 0
web/templates/pages/edit_user.php

@@ -221,6 +221,12 @@
 							?>
 						</select>
 					</div>
+					<div class="form-check u-mb10">
+						<input class="form-check-input" type="checkbox" name="v_shell_jail_enabled" id="v_shell_jail_enabled" value="yes" <?php if (htmlentities(trim($v_shell_jail_enabled, "'")) == "yes") echo 'checked' ?>>
+						<label for="v_shell_jail_enabled">
+							<?= _("Jail User Shell") ?>
+						</label>
+					</div>
 					<div class="u-mb10">
 						<label for="v_phpcli" class="form-label"><?= _("PHP CLI Version") ?></label>
 						<select class="form-select" name="v_phpcli" id="v_phpcli">