2023-10-26 15:54:56 +00:00
#!/bin/bash
2025-04-10 16:07:08 +00:00
# Set up logging to file only
2025-04-10 15:23:32 +00:00
LOG_DIR = "/home/jelastic/add-sftp-user-addon/logs"
LOG_FILE = " $LOG_DIR /script_output.log "
ERROR_LOG = " $LOG_DIR /errors.log "
OPERATION_LOG = " $LOG_DIR /operations.log "
DEBUG_LOG = " $LOG_DIR /debug.log "
2025-04-10 16:07:08 +00:00
DEBUG = ${ 4 :- 0 }
2025-04-10 15:23:32 +00:00
2025-04-10 16:07:08 +00:00
# Ensure log directory exists
mkdir -p $LOG_DIR & >/dev/null
# Function to log ONLY to file, not to stdout
log_to_file( ) {
2023-11-03 14:35:15 +00:00
local level = ${ 1 :- INFO }
local message = ${ 2 }
2023-11-01 06:42:52 +00:00
local timestamp = $( date +"%Y-%m-%d %H:%M:%S" )
2025-04-10 16:07:08 +00:00
local script_id = " $( date +%Y%m%d%H%M%S) - $$ "
2025-04-10 15:23:32 +00:00
2025-04-10 16:07:08 +00:00
echo " [ $script_id ] $timestamp [ $level ] $message " >> " $LOG_FILE "
2025-04-10 15:23:32 +00:00
2025-04-10 16:07:08 +00:00
# Log errors to error log
if [ [ " $level " = = "ERROR" || " $level " = = "WARNING" ] ] ; then
echo " [ $script_id ] $timestamp [ $level ] $message " >> " $ERROR_LOG "
2025-04-10 15:23:32 +00:00
fi
2025-04-10 16:07:08 +00:00
# Log success to operation log
if [ [ " $level " = = "INFO" || " $level " = = "SUCCESS" ] ] ; then
echo " [ $script_id ] $timestamp [ $level ] $message " >> " $OPERATION_LOG "
2025-04-10 15:23:32 +00:00
fi
2025-04-10 16:07:08 +00:00
# Log debug messages if enabled
2025-04-10 15:23:32 +00:00
if [ [ " $level " = = "DEBUG" && " $DEBUG " -eq 1 ] ] ; then
2025-04-10 16:07:08 +00:00
echo " [ $script_id ] $timestamp [ $level ] $message " >> " $DEBUG_LOG "
2025-04-10 15:23:32 +00:00
fi
2023-11-03 14:35:15 +00:00
}
2025-04-10 16:07:08 +00:00
# Enhanced logging functions - file only
log( ) {
log_to_file "INFO" " $1 "
}
2023-11-03 14:35:15 +00:00
log_error( ) {
2025-04-10 16:07:08 +00:00
log_to_file "ERROR" " $1 "
2023-11-03 14:35:15 +00:00
}
log_warning( ) {
2025-04-10 16:07:08 +00:00
log_to_file "WARNING" " $1 "
2023-11-03 14:35:15 +00:00
}
log_debug( ) {
if [ " $DEBUG " -eq 1 ] ; then
2025-04-10 16:07:08 +00:00
log_to_file "DEBUG" " $1 "
2023-11-01 06:42:52 +00:00
fi
}
2025-04-10 15:23:32 +00:00
log_success( ) {
2025-04-10 16:07:08 +00:00
log_to_file "SUCCESS" " $1 "
2025-04-10 15:23:32 +00:00
}
# Log system information for debugging context
log_system_info( ) {
log_debug "============= SYSTEM INFORMATION ============="
log_debug " Operating System: $( cat /etc/os-release | grep PRETTY_NAME | cut -d= -f2 | tr -d '\"' ) "
log_debug " Kernel: $( uname -r) "
log_debug " SSH Version: $( ssh -V 2>& 1) "
log_debug " SSH Config Status: $( systemctl status sshd | grep Active | awk '{print $2}' ) "
log_debug " Script Parameters: username= $USERNAME , ssh_enabled= $SSH_ENABLED "
log_debug "=============================================="
}
# Function to log command execution with result
log_cmd( ) {
local cmd = " $1 "
local cmd_desc = " $2 "
log_debug " Executing: $cmd_desc "
log_debug " Command: $cmd "
# Execute command and capture output and status
local output
output = $( eval " $cmd " 2>& 1)
local status = $?
if [ $status -eq 0 ] ; then
log_debug " Command succeeded: $cmd_desc "
if [ -n " $output " ] ; then
log_debug " Output: $output "
fi
else
log_error " Command failed ( $status ): $cmd_desc "
log_error " Error output: $output "
fi
return $status
}
# Fix SFTP configuration for Jelastic Virtuozzo LLSMP
fix_sftp_config( ) {
log "Checking and fixing SSH configuration for SFTP access"
# Create a backup of the original config
log_cmd " cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak. $( date +%Y%m%d%H%M%S) " "Creating backup of original sshd_config"
# Fix the malformed SFTP subsystem line - using safer sed approach
if grep -q "Subsystemsftp" /etc/ssh/sshd_config; then
log "Fixing malformed SFTP subsystem configuration"
# Use a temporary file for safer editing
if log_cmd "sed 's|Subsystemsftp/usr/libexec/openssh/sftp-server|Subsystem sftp /usr/libexec/openssh/sftp-server|g' /etc/ssh/sshd_config > /etc/ssh/sshd_config.new" "Fixing malformed Subsystem line" ; then
log_cmd "mv /etc/ssh/sshd_config.new /etc/ssh/sshd_config" "Applying fixed configuration"
log_success "Fixed malformed Subsystem line"
else
log_error "Failed to fix Subsystem line, reverting to backup"
log_cmd " cp /etc/ssh/sshd_config.bak. $( ls -t /etc/ssh/sshd_config.bak.* | head -1 | awk -F/ '{print $NF}' ) /etc/ssh/sshd_config " "Restoring backup"
fi
fi
# Enable password authentication globally if it's set to no
if grep -q "^PasswordAuthentication no" /etc/ssh/sshd_config; then
log "Enabling password authentication in SSH"
if log_cmd "sed 's/^PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config > /etc/ssh/sshd_config.new" "Enabling password authentication" ; then
log_cmd "mv /etc/ssh/sshd_config.new /etc/ssh/sshd_config" "Applying configuration with password authentication"
log_success "Enabled password authentication"
else
log_error "Failed to enable password authentication, reverting to backup"
log_cmd " cp /etc/ssh/sshd_config.bak. $( ls -t /etc/ssh/sshd_config.bak.* | head -1 | awk -F/ '{print $NF}' ) /etc/ssh/sshd_config " "Restoring backup"
fi
fi
# Remove any existing incomplete/duplicate Match Group sftpusers blocks
if grep -q "Match Group sftpusers" /etc/ssh/sshd_config; then
log "Removing existing Match Group sftpusers blocks to prevent duplicates"
# This complex sed pattern removes the Match Group sftpusers block completely
if log_cmd " sed '/^Match Group sftpusers/,/^Match\|^[[:space:]]* $/d' /etc/ssh/sshd_config > /etc/ssh/sshd_config.new " "Removing existing Match Group blocks" ; then
log_cmd "mv /etc/ssh/sshd_config.new /etc/ssh/sshd_config" "Applying cleaned configuration"
log_success "Removed existing Match Group sftpusers blocks"
else
log_error "Failed to remove existing Match Group blocks, reverting to backup"
log_cmd " cp /etc/ssh/sshd_config.bak. $( ls -t /etc/ssh/sshd_config.bak.* | head -1 | awk -F/ '{print $NF}' ) /etc/ssh/sshd_config " "Restoring backup"
fi
fi
# Configure SFTP chroot jail cleanly at the end of the file
log "Adding fresh SFTP chroot configuration for sftpusers group"
cat >> /etc/ssh/sshd_config << EOF
# SFTP chroot configuration added by SFTP addon
Match Group sftpusers
ChrootDirectory /home/sftpusers/%u
ForceCommand internal-sftp
PasswordAuthentication yes
AllowTcpForwarding no
X11Forwarding no
EOF
# Remove duplicate lines and verify config
log "Cleaning up configuration file"
if log_cmd "awk '!seen[\$0]++' /etc/ssh/sshd_config > /etc/ssh/sshd_config.new" "Removing duplicate lines" ; then
log_cmd "mv /etc/ssh/sshd_config.new /etc/ssh/sshd_config" "Applying deduplicated configuration"
log_success "Removed duplicate lines from configuration"
else
log_error "Failed to remove duplicate lines, reverting to backup"
log_cmd " cp /etc/ssh/sshd_config.bak. $( ls -t /etc/ssh/sshd_config.bak.* | head -1 | awk -F/ '{print $NF}' ) /etc/ssh/sshd_config " "Restoring backup"
fi
# Verify the configuration before restarting
log "Verifying SSH configuration before restart"
if log_cmd "sshd -t" "Validating sshd configuration" ; then
log_success "SSH configuration is valid, restarting service"
log_cmd "systemctl restart sshd" "Restarting SSH service"
else
log_error "SSH configuration is INVALID, reverting to backup"
log_cmd " cp /etc/ssh/sshd_config.bak. $( ls -t /etc/ssh/sshd_config.bak.* | head -1 | awk -F/ '{print $NF}' ) /etc/ssh/sshd_config " "Restoring backup"
log "Restarting SSH with original configuration"
log_cmd "systemctl restart sshd" "Restarting SSH service with original config"
return 1
fi
return 0
}
2025-04-07 17:00:14 +00:00
validate_username( ) {
local username = $1
2025-04-10 15:23:32 +00:00
log_debug " Validating username: $username "
2025-04-07 17:47:19 +00:00
if ! [ [ $username = ~ ^[ a-zA-Z0-9_] { 3,32} $ ] ] ; then
log_error "Invalid username format. Username must be 3-32 characters long and contain only letters, numbers, and underscores."
2025-04-07 17:00:14 +00:00
return 1
fi
2025-04-10 15:23:32 +00:00
log_debug "Username validation passed"
2025-04-07 17:00:14 +00:00
return 0
2023-11-02 17:26:02 +00:00
}
2023-10-30 13:35:32 +00:00
2025-04-10 16:07:08 +00:00
# Fix directory permissions for chroot
fix_chroot_permissions( ) {
# Check and fix /home permissions
if [ -d "/home" ] ; then
current_mode = $( stat -c "%a" /home)
current_owner = $( stat -c "%U:%G" /home)
log " Checking /home directory permissions - Current: $current_owner $current_mode "
# /home must be owned by root and not writable by others
if [ " $current_mode " != "755" ] || [ " $current_owner " != "root:root" ] ; then
log "Fixing /home directory permissions for chroot"
chown root:root /home
chmod 755 /home
fi
fi
# Check and fix /home/sftpusers permissions
if [ -d "/home/sftpusers" ] ; then
current_mode = $( stat -c "%a" /home/sftpusers)
current_owner = $( stat -c "%U:%G" /home/sftpusers)
log " Checking /home/sftpusers directory permissions - Current: $current_owner $current_mode "
# /home/sftpusers must be owned by root and not writable by others
if [ " $current_mode " != "755" ] || [ " $current_owner " != "root:root" ] ; then
log "Fixing /home/sftpusers directory permissions for chroot"
chown root:root /home/sftpusers
chmod 755 /home/sftpusers
fi
fi
}
# Main script
2023-10-30 17:09:45 +00:00
USERNAME = $1
PASSWORD = $2
2025-04-10 15:23:32 +00:00
SSH_ENABLED = ${ 3 :- false }
2025-04-10 16:07:08 +00:00
# Log to file only
log "======== STARTING SFTP USER SETUP ========"
log " Script started with username: $USERNAME , ssh_enabled: $SSH_ENABLED "
2023-11-03 14:35:15 +00:00
2025-04-10 15:23:32 +00:00
# Log system information
log_system_info
# Fix SFTP configuration
log "Phase 1: Configuring SSH/SFTP service"
if ! fix_sftp_config; then
log_error "Failed to configure SSH/SFTP service, exiting"
exit 1
fi
log_success "SSH/SFTP service configuration completed"
2023-10-26 15:54:56 +00:00
2025-04-10 16:07:08 +00:00
# Fix directory permissions for chroot
log "Phase 1.1: Fixing directory permissions for chroot"
fix_chroot_permissions
log_success "Directory permissions fixed for chroot"
2025-04-07 17:47:19 +00:00
# Validate username format
2025-04-10 15:23:32 +00:00
log "Phase 2: Validating username"
2025-04-07 17:00:14 +00:00
if ! validate_username " $USERNAME " ; then
2025-04-10 15:23:32 +00:00
log_error "Username validation failed, exiting"
2025-04-07 17:00:14 +00:00
exit 1
fi
2025-04-10 15:23:32 +00:00
log_success "Username validation passed"
2025-04-07 17:00:14 +00:00
# Check if user already exists
2025-04-10 15:23:32 +00:00
log "Phase 3: Checking if user already exists"
2025-04-07 17:00:14 +00:00
if id " $USERNAME " & >/dev/null; then
log_error " Username $USERNAME already exists. Please choose a different username. "
exit 1
fi
2025-04-10 15:23:32 +00:00
log_success "Username is available for creation"
2023-11-02 17:48:26 +00:00
2023-11-01 10:54:29 +00:00
USER_HOME = " /home/sftpusers/ $USERNAME "
2023-10-30 16:13:29 +00:00
ROOT_DIRECTORY = "/var/www/webroot/ROOT"
2025-04-10 15:23:32 +00:00
log_debug " Setting paths - USER_HOME: $USER_HOME , ROOT_DIRECTORY: $ROOT_DIRECTORY "
2023-11-01 06:42:52 +00:00
2025-04-10 15:23:32 +00:00
# Create the sftpusers group if it doesn't exist
log "Phase 4: Setting up groups"
if ! getent group sftpusers > /dev/null; then
log "Creating sftpusers group for SFTP chroot access"
log_cmd "groupadd sftpusers" "Creating sftpusers group"
2023-11-03 14:35:15 +00:00
fi
2025-04-10 15:23:32 +00:00
log_success "Group setup completed"
2023-11-03 14:35:15 +00:00
2023-11-03 15:33:26 +00:00
# Ensure the parent directory for user home directories exists
2025-04-10 15:23:32 +00:00
log "Phase 5: Setting up directories"
2023-11-03 15:33:26 +00:00
if [ ! -d "/home/sftpusers" ] ; then
2025-04-10 15:23:32 +00:00
log "Creating /home/sftpusers directory"
log_cmd "mkdir -p /home/sftpusers" "Creating /home/sftpusers directory"
log_cmd "chown root:root /home/sftpusers" "Setting ownership for /home/sftpusers"
log_cmd "chmod 755 /home/sftpusers" "Setting permissions for /home/sftpusers"
2023-11-03 14:35:15 +00:00
fi
2025-04-10 15:23:32 +00:00
log_success "Directory setup completed"
2023-11-03 14:35:15 +00:00
2025-04-10 15:23:32 +00:00
# Create the user account
log "Phase 6: Creating user account"
if [ " $SSH_ENABLED " = "true" ] ; then
log "Creating user with SSH access"
log_cmd " useradd -d $USER_HOME -m -s /bin/bash $USERNAME " "Creating user with bash shell"
2023-11-03 15:23:56 +00:00
else
2025-04-10 15:23:32 +00:00
log "Creating user with SFTP-only access"
log_cmd " useradd -d $USER_HOME -m -s /sbin/nologin $USERNAME " "Creating user with nologin shell"
2023-11-03 14:35:15 +00:00
fi
2025-04-10 15:23:32 +00:00
log_success "User account created"
# Set password
log "Phase 7: Setting user password"
log_cmd " echo ' $USERNAME : $PASSWORD ' | chpasswd " "Setting user password"
log_success " Password set for user $USERNAME "
# Set up proper directory structure for chroot jail
log "Phase 8: Setting up chroot jail structure"
log_cmd " chown root:root $USER_HOME " "Setting ownership for chroot directory"
log_cmd " chmod 755 $USER_HOME " "Setting permissions for chroot directory"
2023-10-26 15:54:56 +00:00
2025-04-10 15:23:32 +00:00
# Create writable data directory for user
log_cmd " mkdir -p $USER_HOME /data " "Creating data directory"
log_cmd " chown $USERNAME : $USERNAME $USER_HOME /data " "Setting ownership for data directory"
log_cmd " chmod 775 $USER_HOME /data " "Setting permissions for data directory"
2023-11-01 05:22:36 +00:00
2025-04-10 16:07:08 +00:00
# Create mount point for webroot (using bind mount instead of symlink)
log "Phase 9: Setting up webroot access via bind mount"
log_cmd " mkdir -p $USER_HOME /data/ROOT " "Creating ROOT mount point"
log_cmd " mount --bind $ROOT_DIRECTORY $USER_HOME /data/ROOT " "Binding webroot to user's ROOT directory"
# Add mount to fstab to persist across reboots
if ! grep -q " $ROOT_DIRECTORY $USER_HOME /data/ROOT " /etc/fstab; then
log_cmd " echo \" $ROOT_DIRECTORY $USER_HOME /data/ROOT none bind 0 0\" >> /etc/fstab " "Adding bind mount to fstab"
fi
log_success "Created bind mount for webroot access"
2023-10-26 15:54:56 +00:00
2025-04-10 15:23:32 +00:00
# Add user to the required groups
log "Phase 10: Adding user to groups"
log_cmd " usermod -aG sftpusers $USERNAME " "Adding user to sftpusers group"
log_success " Added $USERNAME to sftpusers group for chroot access "
2023-11-01 16:48:25 +00:00
2025-04-10 15:23:32 +00:00
log_cmd " usermod -aG litespeed $USERNAME " "Adding user to litespeed group"
log_success " Added $USERNAME to litespeed group for file access "
2023-10-27 16:19:22 +00:00
2025-04-10 15:23:32 +00:00
# Create welcome file
log "Phase 11: Creating welcome file"
cat > $USER_HOME /data/welcome.txt << EOF
Welcome to your SFTP account on Jelastic!
2023-10-26 15:54:56 +00:00
2025-04-10 15:23:32 +00:00
Your account has been set up with the following details:
2023-11-01 06:42:52 +00:00
2025-04-10 15:23:32 +00:00
Username: $USERNAME
Home Directory: $USER_HOME
Web Root: $USER_HOME /data/ROOT ( symlink to $ROOT_DIRECTORY )
2023-11-01 06:42:52 +00:00
2025-04-10 15:23:32 +00:00
For help and support, please contact your system administrator.
EOF
log_cmd " chown $USERNAME : $USERNAME $USER_HOME /data/welcome.txt " "Setting welcome file ownership"
log_cmd " chmod 644 $USER_HOME /data/welcome.txt " "Setting welcome file permissions"
log_success "Welcome file created"
2023-11-01 05:22:36 +00:00
2025-04-10 16:07:08 +00:00
# Always export variables directly with no console output
export CREATED_USERNAME = " $USERNAME "
export CREATED_PASSWORD = " $PASSWORD "
# All logging should be to file only
2025-04-10 15:23:32 +00:00
log_success " Script completed successfully for user $USERNAME "
log "======== SFTP USER SETUP COMPLETE ========"
2023-11-02 17:48:26 +00:00
2025-04-10 16:07:08 +00:00
exit 0