version: 0.4 id: addsftp type: update description: An addon to add new SFTP users for Jelastic Virtuozzo LLSMP environments. It manages user accounts with secure SFTP access and optional SSH access with proper chroot jailing. name: Add SFTP User for Jelastic targetNodes: nodeGroup: cp settings: sfpform: submitUnchanged: true fields: - type: displayfield name: infoField caption: Root Directory /var/www/webroot/ROOT/ description: "Files will be accessible in a data/ROOT directory within the user's home" required: false - type: string name: custom_username caption: Custom Username description: "Enter a custom username (3-32 characters, alphanumeric + underscore only)" required: true regex: ^[a-zA-Z0-9_]{3,32}$ regexText: "Username must be 3-32 characters long and contain only letters, numbers, and underscores" - type: checkbox name: allow caption: Accept User Creation default: false required: false - type: checkbox name: enable_ssh caption: Also enable SSH access default: false required: false tip: "If enabled, the user will have both SFTP and SSH access. Otherwise, only SFTP access will be granted." manageUserForm: fields: - type: string name: manage_username caption: Username to Manage description: "Enter the username you want to manage." required: true regex: ^[a-zA-Z0-9_]{3,32}$ regexText: "Username must be 3-32 characters long and contain only letters, numbers, and underscores" - type: string name: custom_password caption: New Password description: "Enter a new password for the user (leave empty to generate random password)" required: false globals: username: ${settings.custom_username} password: ${fn.password(min)} sftpHost: ${env.domain} sftpPort: 22 onInstall: - cmd [cp]: user: root commands: |- # Create log directory structure mkdir -p /home/jelastic/add-sftp-user-addon/logs/{operations,errors,debug} chmod -R 755 /home/jelastic/add-sftp-user-addon/logs # Setup log files with proper permissions touch /home/jelastic/add-sftp-user-addon/logs/script_output.log touch /home/jelastic/add-sftp-user-addon/logs/operations/install.log touch /home/jelastic/add-sftp-user-addon/logs/errors/install.log chmod 644 /home/jelastic/add-sftp-user-addon/logs/*.log chmod 644 /home/jelastic/add-sftp-user-addon/logs/*/*.log # Create a function for structured logging cat > /home/jelastic/add-sftp-user-addon/log_helper.sh << 'EOF' #!/bin/bash LOG_DIR="/home/jelastic/add-sftp-user-addon/logs" SCRIPT_ID="$(date +%Y%m%d%H%M%S)-$$" # Main logging function jps_log() { local level=${1:-INFO} local message=${2} local log_file=${3:-$LOG_DIR/script_output.log} local timestamp=$(date +"%Y-%m-%d %H:%M:%S") echo "[$SCRIPT_ID] $timestamp [$level] $message" >> "$log_file" # Also log to stdout echo "[$level] $message" # Log errors to error log if [[ "$level" == "ERROR" || "$level" == "WARNING" ]]; then echo "[$SCRIPT_ID] $timestamp [$level] $message" >> "$LOG_DIR/errors/$(basename "$log_file")" fi # Log successful operations if [[ "$level" == "INFO" || "$level" == "SUCCESS" ]]; then echo "[$SCRIPT_ID] $timestamp [$level] $message" >> "$LOG_DIR/operations/$(basename "$log_file")" fi } # Log command execution jps_log_cmd() { local cmd="$1" local desc="$2" local log_file=${3:-$LOG_DIR/script_output.log} jps_log "DEBUG" "Executing: $desc" "$log_file" jps_log "DEBUG" "Command: $cmd" "$log_file" # Execute command and capture output and status local output output=$(eval "$cmd" 2>&1) local status=$? if [ $status -eq 0 ]; then jps_log "DEBUG" "Command succeeded: $desc" "$log_file" [ -n "$output" ] && jps_log "DEBUG" "Output: $output" "$log_file" else jps_log "ERROR" "Command failed ($status): $desc" "$log_file" jps_log "ERROR" "Error output: $output" "$log_file" fi return $status } # Log system information jps_log_system_info() { local log_file=${1:-$LOG_DIR/script_output.log} jps_log "DEBUG" "============= SYSTEM INFORMATION =============" "$log_file" jps_log "DEBUG" "Operating System: $(cat /etc/os-release | grep PRETTY_NAME | cut -d= -f2 | tr -d '\"')" "$log_file" jps_log "DEBUG" "Kernel: $(uname -r)" "$log_file" jps_log "DEBUG" "SSH Version: $(ssh -V 2>&1)" "$log_file" jps_log "DEBUG" "SSH Status: $(systemctl status sshd | grep Active | awk '{print $2}')" "$log_file" jps_log "DEBUG" "=============================================" "$log_file" } # Function to get detailed user information including creation date # This replaces the functionality of the retired userlogs.sh script jps_get_user_info() { local log_file=${1:-$LOG_DIR/list_users.log} jps_log "INFO" "Retrieving detailed user information" "$log_file" # Get SFTP users local users=$(find /home/sftpusers -maxdepth 1 -mindepth 1 -type d -exec basename {} \;) if [ -z "$users" ]; then jps_log "INFO" "No SFTP users found" "$log_file" return 0 fi jps_log "INFO" "Found users, retrieving creation dates" "$log_file" local result="" # Process each user for user in $users; do # Get creation date from directory timestamp local creation_date=$(stat -c "%y" "/home/sftpusers/$user" 2>/dev/null | cut -d. -f1) # Try to get last password change as fallback if [ -z "$creation_date" ]; then if id "$user" &>/dev/null; then creation_date=$(chage -l "$user" 2>/dev/null | grep "Last password change" | cut -d: -f2) fi fi if [ -n "$creation_date" ]; then result="${result}Username: $user - Created: $creation_date\n" else result="${result}Username: $user - Created: Unknown\n" fi done if [ -n "$result" ]; then jps_log "SUCCESS" "User information retrieved successfully" "$log_file" echo -e "$result" else jps_log "WARNING" "Could not retrieve user information" "$log_file" echo "" fi } # Make the logging script executable chmod +x /home/jelastic/add-sftp-user-addon/log_helper.sh # Download the SFTP script wget https://deploy-proxy.mightybox.io/addons/add-sftp-user/raw/branch/main/add-sftp.sh -O /home/jelastic/add-sftp-user-addon/add-sftp.sh chmod +x /home/jelastic/add-sftp-user-addon/add-sftp.sh # Source the logging helper source /home/jelastic/add-sftp-user-addon/log_helper.sh # Log installation started jps_log "INFO" "======== SFTP ADDON INSTALLATION STARTED ========" "install.log" jps_log_system_info "install.log" # Install SFTP addon on Jelastic environment jps_log_cmd "mkdir -p /home/jelastic/add-sftp-user-addon/" "Creating log directory structure" jps_log_cmd "mkdir -p /home/jelastic/add-sftp-user-addon/logs" "Creating log directory structure" jps_log_cmd "touch /home/jelastic/add-sftp-user-addon/logs/script_output.log" "Creating script_output.log" jps_log_cmd "wget https://deploy-proxy.mightybox.io/addons/add-sftp-user/raw/branch/main/add-sftp.sh -O /home/jelastic/add-sftp-user-addon/add-sftp.sh" "Downloading SFTP script" jps_log_cmd "chmod +x /home/jelastic/add-sftp-user-addon/*.sh" "Making SFTP script executable" jps_log_cmd "echo \"$(date) - Installing SFTP addon on Jelastic environment\" >> /home/jelastic/add-sftp-user-addon/logs/script_output.log" "Logging installation" - cmd[cp]: user: root commands: |- # Source the logging helper source /home/jelastic/add-sftp-user-addon/log_helper.sh # Create a backup of the original SSH config jps_log "INFO" "Creating backup of original sshd_config" "install.log" jps_log_cmd "cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%Y%m%d%H%M%S)" "Creating backup of SSH config" "install.log" # Fix the malformed SFTP subsystem configuration using safer approach if grep -q "Subsystemsftp" /etc/ssh/sshd_config; then jps_log "INFO" "Found malformed SFTP subsystem configuration, fixing it" "install.log" if jps_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 SFTP configuration" "install.log"; then jps_log_cmd "mv /etc/ssh/sshd_config.new /etc/ssh/sshd_config" "Applying fixed configuration" "install.log" jps_log "SUCCESS" "Fixed malformed SFTP subsystem configuration" "install.log" else jps_log "ERROR" "Failed to fix SFTP subsystem, reverting to backup" "install.log" jps_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" "install.log" fi else jps_log "INFO" "SFTP subsystem configuration is correct" "install.log" fi # Enable password authentication globally if it's set to no if grep -q "^PasswordAuthentication no" /etc/ssh/sshd_config; then jps_log "INFO" "Password authentication is disabled, enabling it" "install.log" if jps_log_cmd "sed 's/^PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config > /etc/ssh/sshd_config.new" "Enabling password authentication" "install.log"; then jps_log_cmd "mv /etc/ssh/sshd_config.new /etc/ssh/sshd_config" "Applying configuration with password authentication" "install.log" jps_log "SUCCESS" "Enabled global password authentication" "install.log" else jps_log "ERROR" "Failed to enable password authentication, reverting to backup" "install.log" jps_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" "install.log" fi else jps_log "INFO" "Password authentication is already enabled" "install.log" fi # Remove any existing duplicate Match Group sftpusers blocks if grep -q "Match Group sftpusers" /etc/ssh/sshd_config; then jps_log "INFO" "Found existing Match Group sftpusers configuration, removing it" "install.log" if jps_log_cmd "sed '/^Match Group sftpusers/,/^Match\|^[[:space:]]*$/d' /etc/ssh/sshd_config > /etc/ssh/sshd_config.new" "Removing existing Match Group blocks" "install.log"; then jps_log_cmd "mv /etc/ssh/sshd_config.new /etc/ssh/sshd_config" "Applying cleaned configuration" "install.log" jps_log "SUCCESS" "Removed existing Match Group sftpusers blocks" "install.log" else jps_log "ERROR" "Failed to remove existing Match blocks, reverting to backup" "install.log" jps_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" "install.log" fi else jps_log "INFO" "No existing Match Group sftpusers configuration found" "install.log" fi # Add SFTP chroot configuration at the end jps_log "INFO" "Adding SFTP chroot configuration" "install.log" jps_log_cmd "echo -e '\n# SFTP chroot configuration added by SFTP addon\nMatch Group sftpusers\n ChrootDirectory /home/sftpusers/%u\n ForceCommand internal-sftp\n PasswordAuthentication yes\n AllowTcpForwarding no\n X11Forwarding no' >> /etc/ssh/sshd_config" "Adding SFTP chroot configuration" "install.log" jps_log "SUCCESS" "Added SFTP chroot configuration" "install.log" # Create sftpusers group for chroot jailing if ! getent group sftpusers > /dev/null; then jps_log "INFO" "sftpusers group doesn't exist, creating it" "install.log" jps_log_cmd "groupadd sftpusers" "Creating sftpusers group" "install.log" jps_log "SUCCESS" "Created sftpusers group" "install.log" else jps_log "INFO" "sftpusers group already exists" "install.log" fi # Create sftpusers directory if [ ! -d "/home/sftpusers" ]; then jps_log "INFO" "Creating /home/sftpusers directory" "install.log" jps_log_cmd "mkdir -p /home/sftpusers" "Creating sftpusers directory" "install.log" jps_log_cmd "chown root:root /home/sftpusers" "Setting ownership for sftpusers directory" "install.log" jps_log_cmd "chmod 755 /home/sftpusers" "Setting permissions for sftpusers directory" "install.log" jps_log "SUCCESS" "Created /home/sftpusers directory" "install.log" else jps_log "INFO" "/home/sftpusers directory already exists" "install.log" fi # Clean up configuration - remove duplicate lines jps_log "INFO" "Cleaning up configuration file" "install.log" if jps_log_cmd "awk '!seen[\$0]++' /etc/ssh/sshd_config > /etc/ssh/sshd_config.new" "Removing duplicate lines" "install.log"; then jps_log_cmd "mv /etc/ssh/sshd_config.new /etc/ssh/sshd_config" "Applying deduplicated configuration" "install.log" jps_log "SUCCESS" "Removed duplicate lines from configuration" "install.log" else jps_log "ERROR" "Failed to clean up configuration, reverting to backup" "install.log" jps_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" "install.log" fi # Verify configuration is valid before applying jps_log "INFO" "Verifying SSH configuration" "install.log" if jps_log_cmd "sshd -t" "Validating sshd configuration" "install.log"; then jps_log "SUCCESS" "SSH configuration is valid, applying changes" "install.log" jps_log_cmd "systemctl restart sshd" "Restarting SSH service" "install.log" else jps_log "ERROR" "SSH configuration is INVALID, reverting to backup" "install.log" jps_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" "install.log" jps_log_cmd "systemctl restart sshd" "Restarting SSH service with original config" "install.log" fi jps_log "SUCCESS" "======== SFTP ADDON CONFIGURATION COMPLETED ========" "install.log" - cmd[cp]: user: root commands: - systemctl restart sshd - api: environment.control.RestartContainer nodeGroup: cp nodeid: ${nodes.cp.id} - return: installSuccess menu: confirmText: "Do you want to list all users?" loadingText: "Loading users..." action: "list_users" caption: "List Users" successText: "Users listed successfully!" logsNodeGroup: cp actions: add_sftp_user: - if ('${settings.enable_ssh}' == 'true'): setGlobals: ssh_enabled: "true" - if ('${settings.enable_ssh}' != 'true'): setGlobals: ssh_enabled: "false" - cmd[cp]: user: root commands: |- # Source the logging helper source /home/jelastic/add-sftp-user-addon/log_helper.sh # Log the action jps_log "INFO" "======== STARTING SFTP USER CREATION ========" "user_creation.log" jps_log "INFO" "Creating user: ${globals.username}, SSH enabled: ${globals.ssh_enabled}" "user_creation.log" # Run the SFTP user creation script with logging jps_log_cmd "bash /home/jelastic/add-sftp-user-addon/add-sftp.sh ${globals.username} ${globals.password} ${globals.ssh_enabled}" "Running add-sftp.sh script" "user_creation.log" # Capture the created username and password jps_log "INFO" "Retrieving created username" "user_creation.log" CREATED_USERNAME=$(echo $CREATED_USERNAME) jps_log "DEBUG" "Created username: $CREATED_USERNAME" "user_creation.log" jps_log "INFO" "Retrieving created password" "user_creation.log" CREATED_PASSWORD=$(echo $CREATED_PASSWORD) jps_log "DEBUG" "Password retrieved" "user_creation.log" # Export for JPS echo $CREATED_USERNAME - setGlobals: username: ${response.out} - cmd[cp]: user: root commands: |- # Source the logging helper source /home/jelastic/add-sftp-user-addon/log_helper.sh # Log the action jps_log "SUCCESS" "User ${globals.username} created successfully" "user_creation.log" # Export password echo $CREATED_PASSWORD - setGlobals: password: ${response.out} - cmd[cp]: user: root commands: |- # Source the logging helper source /home/jelastic/add-sftp-user-addon/log_helper.sh # Log completion jps_log "SUCCESS" "======== SFTP USER CREATION COMPLETED ========" "user_creation.log" jps_log "INFO" "Connection details - Host: ${globals.sftpHost}, Port: ${globals.sftpPort}, Username: ${globals.username}" "user_creation.log" - return: type: info message: "Connection Details\n\nSFTP Host: ${globals.sftpHost}\n\nPort: ${globals.sftpPort}\n\nLogin Credentials\n\nUsername: ${globals.username}\n\nPassword: ${globals.password}\n\nNotes:\n- Files are accessible at /data/ROOT inside your SFTP session\n- If you enabled SSH access, you can also log in via SSH" change_password: # Verify user exists - cmd[cp]: user: root commands: |- # Source the logging helper source /home/jelastic/add-sftp-user-addon/log_helper.sh # Log start of password change jps_log "INFO" "======== STARTING PASSWORD CHANGE ========" "password_change.log" jps_log "INFO" "Verifying user exists: ${settings.manage_username}" "password_change.log" # Check if user exists if jps_log_cmd "id ${settings.manage_username} &>/dev/null" "Checking if user exists" "password_change.log"; then jps_log "INFO" "User ${settings.manage_username} exists" "password_change.log" exit 0 else jps_log "ERROR" "User ${settings.manage_username} does not exist" "password_change.log" echo "User does not exist" exit 1 fi - if ("${response.exitStatus}" != "0"): return: type: error message: "User ${settings.manage_username} does not exist." # Use custom password if provided, otherwise generate one - if ('${settings.custom_password}' != ''): setGlobals: password: ${settings.custom_password} - if ('${settings.custom_password}' == ''): setGlobals: password: ${fn.password(12)} # Update password - cmd[cp]: user: root commands: |- # Source the logging helper source /home/jelastic/add-sftp-user-addon/log_helper.sh # Log password change jps_log "INFO" "Changing password for user: ${settings.manage_username}" "password_change.log" # Update password if jps_log_cmd "echo \"${settings.manage_username}:${globals.password}\" | chpasswd" "Changing user password" "password_change.log"; then jps_log "SUCCESS" "Password changed successfully for ${settings.manage_username}" "password_change.log" else jps_log "ERROR" "Failed to change password for ${settings.manage_username}" "password_change.log" exit 1 fi # Log password change to main log jps_log_cmd "echo \"Password changed for ${settings.manage_username} at $(date)\" >> /home/jelastic/add-sftp-user-addon/logs/script_output.log" "Recording password change in main log" "password_change.log" # Log completion jps_log "SUCCESS" "======== PASSWORD CHANGE COMPLETED ========" "password_change.log" - if ("${response.exitStatus}" != "0"): return: passwordChangeError - return: passwordChangeSuccess delete_user: # Verify user exists first - cmd[cp]: user: root commands: |- # Source the logging helper source /home/jelastic/add-sftp-user-addon/log_helper.sh # Log start of user deletion jps_log "INFO" "======== STARTING USER DELETION ========" "user_deletion.log" jps_log "INFO" "Verifying user exists: ${settings.manage_username}" "user_deletion.log" # Check if user exists if jps_log_cmd "id ${settings.manage_username} &>/dev/null" "Checking if user exists" "user_deletion.log"; then jps_log "INFO" "User ${settings.manage_username} exists" "user_deletion.log" else jps_log "ERROR" "User ${settings.manage_username} does not exist" "user_deletion.log" echo "User does not exist" exit 1 fi - if ("${response.exitStatus}" != "0"): return: type: error message: "User ${settings.manage_username} does not exist." # Check if user is in sftpusers directory - cmd[cp]: user: root commands: |- # Source the logging helper source /home/jelastic/add-sftp-user-addon/log_helper.sh # Verify home directory exists jps_log "INFO" "Checking home directory for: ${settings.manage_username}" "user_deletion.log" if jps_log_cmd "test -d /home/sftpusers/${settings.manage_username}" "Checking user home directory" "user_deletion.log"; then jps_log "INFO" "Home directory found: /home/sftpusers/${settings.manage_username}" "user_deletion.log" else jps_log "ERROR" "Home directory not found for user: ${settings.manage_username}" "user_deletion.log" echo "User home directory not found" exit 1 fi - if ("${response.exitStatus}" != "0"): return: type: error message: "User ${settings.manage_username} exists but their home directory was not found." # Perform deletion - cmd[cp]: user: root commands: |- # Source the logging helper source /home/jelastic/add-sftp-user-addon/log_helper.sh # Log deletion process jps_log "INFO" "Starting deletion of user: ${settings.manage_username}" "user_deletion.log" # Delete user account if jps_log_cmd "userdel ${settings.manage_username}" "Deleting user account" "user_deletion.log"; then jps_log "SUCCESS" "User account deleted: ${settings.manage_username}" "user_deletion.log" else jps_log "ERROR" "Failed to delete user account: ${settings.manage_username}" "user_deletion.log" exit 1 fi # Remove home directory if jps_log_cmd "rm -rf /home/sftpusers/${settings.manage_username}" "Removing user home directory" "user_deletion.log"; then jps_log "SUCCESS" "Home directory removed: /home/sftpusers/${settings.manage_username}" "user_deletion.log" else jps_log "WARNING" "Failed to remove home directory for: ${settings.manage_username}" "user_deletion.log" fi # Log to main log file jps_log_cmd "echo \"User ${settings.manage_username} deleted at $(date)\" >> /home/jelastic/add-sftp-user-addon/logs/script_output.log" "Recording user deletion in main log" "user_deletion.log" # Log completion jps_log "SUCCESS" "======== USER DELETION COMPLETED ========" "user_deletion.log" - if ("${response.exitStatus}" != "0"): return: deleteUserError - return: deleteUserSuccess list_users: - cmd[cp]: user: root commands: |- # Source the logging helper source /home/jelastic/add-sftp-user-addon/log_helper.sh # Log list users action jps_log "INFO" "======== LISTING SFTP USERS ========" "list_users.log" # Note: This functionality replaces the retired userlogs.sh script # with a more efficient and integrated approach # Get detailed user information USER_INFO=$(jps_get_user_info "list_users.log") # Output the result if [ -z "$USER_INFO" ]; then jps_log "WARNING" "No SFTP users found" "list_users.log" echo "" else jps_log "SUCCESS" "Retrieved list of SFTP users" "list_users.log" echo "$USER_INFO" fi jps_log "INFO" "======== USER LISTING COMPLETED ========" "list_users.log" - if ("${response.exitStatus}" != "0" || "${response.out}" == ""): return: type: warning message: "No SFTP users found. Use the Add SFTP/SSH User button to create one." - return: listUsers responses: installSuccess: type: success message: "Add SFTP User addon installed successfully for Jelastic Virtuozzo LLSMP" sftpError: type: error message: "Failed to add SFTP user. Please check the server logs for more details." sftpSuccess: type: success message: "Connection Details\n\nSFTP Host: ${globals.sftpHost}\n\nPort: ${globals.sftpPort}\n\nLogin Credentials\n\nUsername: ${globals.username}\n\nPassword: ${globals.password}" passwordChangeError: type: error message: "Failed to change password for ${settings.manage_username}. Check logs for details." passwordChangeSuccess: type: success message: "Password changed successfully for ${settings.manage_username}.\n\n New password: ${globals.password}" deleteUserError: type: error message: "Failed to delete user ${settings.manage_username}. Check logs for details." deleteUserSuccess: type: success message: "User ${settings.manage_username} deleted successfully." noUsersFound: type: error message: "No SFTP users have been created yet." listUsers: type: info message: "${response.out}" buttons: - settings: sfpform action: add_sftp_user caption: Add SFTP/SSH User confirmText: "Are you sure you want to add this SFTP user?" submitButtonText: Add User - settings: manageUserForm action: change_password caption: Change Password confirmText: "Are you sure you want to change the password for this user?" submitButtonText: Change Password - settings: manageUserForm action: delete_user caption: Delete User confirmText: "Are you sure you want to delete this user?" submitButtonText: Delete User onUninstall: - cmd[cp]: user: root commands: - rm -rf /home/jelastic/add-sftp-user-addon/