Fix SSH account creation

main
Anthony 2025-07-30 00:33:46 +08:00
parent 4b5d7d1d8a
commit 32bd773fde
5 changed files with 311 additions and 294 deletions

View File

@ -1,184 +1,15 @@
#!/bin/bash
# Set up logging to file only
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"
DEBUG=${4:-0}
# Set debug mode based on the 4th script argument.
# This must be set BEFORE sourcing the logging library.
export DEBUG=${4:-0}
# Ensure log directory exists
mkdir -p $LOG_DIR &>/dev/null
# Source the canonical logging library.
# The path is relative to this script's location.
source "$(dirname "$0")/scripts/logging.sh"
# Function to log ONLY to file, not to stdout
log_to_file() {
local level=${1:-INFO}
local message=${2}
local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
local script_id="$(date +%Y%m%d%H%M%S)-$$"
echo "[$script_id] $timestamp [$level] $message" >> "$LOG_FILE"
# Log errors to error log
if [[ "$level" == "ERROR" || "$level" == "WARNING" ]]; then
echo "[$script_id] $timestamp [$level] $message" >> "$ERROR_LOG"
fi
# Log success to operation log
if [[ "$level" == "INFO" || "$level" == "SUCCESS" ]]; then
echo "[$script_id] $timestamp [$level] $message" >> "$OPERATION_LOG"
fi
# Log debug messages if enabled
if [[ "$level" == "DEBUG" && "$DEBUG" -eq 1 ]]; then
echo "[$script_id] $timestamp [$level] $message" >> "$DEBUG_LOG"
fi
}
# Enhanced logging functions - file only
log() {
log_to_file "INFO" "$1"
}
log_error() {
log_to_file "ERROR" "$1"
}
log_warning() {
log_to_file "WARNING" "$1"
}
log_debug() {
if [ "$DEBUG" -eq 1 ]; then
log_to_file "DEBUG" "$1"
fi
}
log_success() {
log_to_file "SUCCESS" "$1"
}
# 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
}
# Source the canonical system preparation library.
source "$(dirname "$0")/scripts/system_prep.sh"
validate_username() {
local username=$1
@ -193,39 +24,6 @@ validate_username() {
return 0
}
# 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
USERNAME=$1
PASSWORD=$2
@ -265,21 +63,17 @@ log_success "Pre-flight checks passed"
# Log system information
log_system_info
log_debug "Script Parameters: username=$USERNAME, ssh_enabled=$SSH_ENABLED"
# Fix SFTP configuration
log "Phase 1: Configuring SSH/SFTP service"
if ! fix_sftp_config; then
if ! prepare_sftp_system; then
log_error "Failed to configure SSH/SFTP service, exiting"
echo "ERROR: Failed to configure SSH/SFTP service" >&2
exit 1
fi
log_success "SSH/SFTP service configuration completed"
# Fix directory permissions for chroot
log "Phase 1.1: Fixing directory permissions for chroot"
fix_chroot_permissions
log_success "Directory permissions fixed for chroot"
# Validate username format
log "Phase 2: Validating username"
if ! validate_username "$USERNAME"; then
@ -305,9 +99,14 @@ log_debug "Setting paths - USER_HOME: $USER_HOME, ROOT_DIRECTORY: $ROOT_DIRECTOR
# 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 "Creating sftpusers group for SFTP-only access"
log_cmd "groupadd sftpusers" "Creating sftpusers group"
fi
# Create the sshusers group if it doesn't exist
if ! getent group sshusers > /dev/null; then
log "Creating sshusers group for SFTP+SSH access"
log_cmd "groupadd sshusers" "Creating sshusers group"
fi
log_success "Group setup completed"
# Ensure the parent directory for user home directories exists
@ -390,10 +189,15 @@ log_success "Created bind mount for webroot access"
# 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"
if [ "$SSH_ENABLED" = "true" ]; then
log_cmd "usermod -aG sshusers $USERNAME" "Adding user to sshusers group for SSH+SFTP access"
log_success "Added $USERNAME to sshusers group"
else
log_cmd "usermod -aG sftpusers $USERNAME" "Adding user to sftpusers group for SFTP-only access"
log_success "Added $USERNAME to sftpusers group"
fi
log_cmd "usermod -aG litespeed $USERNAME" "Adding user to litespeed group"
log_cmd "usermod -aG litespeed $USERNAME" "Adding user to litespeed group for file access"
log_success "Added $USERNAME to litespeed group for file access"
# Create welcome file

View File

@ -1,24 +0,0 @@
#!/bin/bash
# Simple logging function for SFTP User Addon
log_message() {
local level="$1"
local message="$2"
local logfile="$3"
local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
echo "[$timestamp][$level] $message" >> "/home/jelastic/add-sftp-user-addon/logs/$logfile"
# Also log to main log
echo "[$timestamp][$level] $message" >> "/home/jelastic/add-sftp-user-addon/logs/script_output.log"
# Log errors/warnings
if [[ "$level" == "ERROR" || "$level" == "WARNING" ]]; then
echo "[$timestamp][$level] $message" >> "/home/jelastic/add-sftp-user-addon/logs/errors.log"
fi
# Log success/info
if [[ "$level" == "INFO" || "$level" == "SUCCESS" ]]; then
echo "[$timestamp][$level] $message" >> "/home/jelastic/add-sftp-user-addon/logs/operations.log"
fi
}

View File

@ -2,7 +2,7 @@ version: 0.6
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
name: Add SFTP User for MB Panel
targetNodes:
nodeGroup: cp
@ -76,26 +76,26 @@ onInstall:
# Download scripts
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
wget https://deploy-proxy.mightybox.io/addons/add-sftp-user/raw/branch/main/log_helper.sh -O /home/jelastic/add-sftp-user-addon/log_helper.sh
wget https://deploy-proxy.mightybox.io/addons/add-sftp-user/raw/branch/main/scripts/logging.sh -O /home/jelastic/add-sftp-user-addon/scripts/logging.sh
wget https://deploy-proxy.mightybox.io/addons/add-sftp-user/raw/branch/main/scripts/system_prep.sh -O /home/jelastic/add-sftp-user-addon/scripts/system_prep.sh
chmod +x /home/jelastic/add-sftp-user-addon/add-sftp.sh
chmod +x /home/jelastic/add-sftp-user-addon/log_helper.sh
chmod +x /home/jelastic/add-sftp-user-addon/scripts/logging.sh
chmod +x /home/jelastic/add-sftp-user-addon/scripts/system_prep.sh
# Very important - fix /home directory permissions for SFTP chroot
echo "$(date) - Checking and fixing /home directory permissions for SFTP chroot" >> /home/jelastic/add-sftp-user-addon/logs/script_output.log
# Source libraries and run the system preparation function
source /home/jelastic/add-sftp-user-addon/scripts/logging.sh
source /home/jelastic/add-sftp-user-addon/scripts/system_prep.sh
# Get current /home permissions
current_owner=$(stat -c "%U:%G" /home)
current_perms=$(stat -c "%a" /home)
echo "Current /home ownership: $current_owner, permissions: $current_perms" >> /home/jelastic/add-sftp-user-addon/logs/script_output.log
log "======== STARTING ADDON INSTALLATION ========"
# Fix ownership and permissions
chown root:root /home
chmod 755 /home
# Create sftpusers group and directory before running full prep
groupadd -f sftpusers
mkdir -p /home/sftpusers
echo "Fixed /home ownership to root:root with 755 permissions" >> /home/jelastic/add-sftp-user-addon/logs/script_output.log
echo "$(date) - Installing SFTP addon on Jelastic environment" >> /home/jelastic/add-sftp-user-addon/logs/script_output.log
prepare_sftp_system
log "======== ADDON INSTALLATION COMPLETE ========"
- cmd[cp]:
user: root
commands: |-
@ -210,18 +210,18 @@ actions:
touch "$LOG_FILE"
# Source the logging helper
source /home/jelastic/add-sftp-user-addon/log_helper.sh
source /home/jelastic/add-sftp-user-addon/scripts/logging.sh
# Log start of process
log_message "INFO" "======== STARTING PASSWORD CHANGE ========" "password_change.log"
log_message "INFO" "Verifying user exists: ${settings.manage_username}" "password_change.log"
log "======== STARTING PASSWORD CHANGE ========"
log "Verifying user exists: ${settings.manage_username}"
# Check if user exists
if id ${settings.manage_username} &>/dev/null; then
log_message "INFO" "User ${settings.manage_username} exists" "password_change.log"
log "User ${settings.manage_username} exists"
exit 0
else
log_message "ERROR" "User ${settings.manage_username} does not exist" "password_change.log"
log_error "User ${settings.manage_username} does not exist"
echo "User does not exist"
exit 1
fi
@ -243,21 +243,21 @@ actions:
user: root
commands: |-
# Source the logging helper
source /home/jelastic/add-sftp-user-addon/log_helper.sh
source /home/jelastic/add-sftp-user-addon/scripts/logging.sh
log_message "INFO" "Changing password for user: ${settings.manage_username}" "password_change.log"
log "Changing password for user: ${settings.manage_username}"
# Change password using echo and chpasswd
if echo "${settings.manage_username}:${globals.password}" | /usr/sbin/chpasswd; then
log_message "SUCCESS" "Password changed successfully for ${settings.manage_username}" "password_change.log"
log_success "Password changed successfully for ${settings.manage_username}"
echo "Password changed for ${settings.manage_username} at $(date)" >> /home/jelastic/add-sftp-user-addon/logs/script_output.log
exit 0
else
log_message "ERROR" "Failed to change password for ${settings.manage_username}" "password_change.log"
log_error "Failed to change password for ${settings.manage_username}"
exit 1
fi
log_message "SUCCESS" "======== PASSWORD CHANGE COMPLETED ========" "password_change.log"
log_success "======== PASSWORD CHANGE COMPLETED ========"
- if ("${response.exitStatus}" != "0"):
return: passwordChangeError
- return: passwordChangeSuccess
@ -271,18 +271,18 @@ actions:
touch "$LOG_FILE"
# Source the logging helper
source /home/jelastic/add-sftp-user-addon/log_helper.sh
source /home/jelastic/add-sftp-user-addon/scripts/logging.sh
# Log start of process
log_message "INFO" "======== STARTING USER DELETION ========" "user_deletion.log"
log_message "INFO" "Verifying user exists: ${settings.manage_username}" "user_deletion.log"
log "======== STARTING USER DELETION ========"
log "Verifying user exists: ${settings.manage_username}"
# Check if user exists
if id ${settings.manage_username} &>/dev/null; then
log_message "INFO" "User ${settings.manage_username} exists" "user_deletion.log"
log "User ${settings.manage_username} exists"
exit 0
else
log_message "ERROR" "User ${settings.manage_username} does not exist" "user_deletion.log"
log_error "User ${settings.manage_username} does not exist"
echo "User does not exist"
exit 1
fi
@ -296,15 +296,15 @@ actions:
user: root
commands: |-
# Source the logging helper
source /home/jelastic/add-sftp-user-addon/log_helper.sh
source /home/jelastic/add-sftp-user-addon/scripts/logging.sh
log_message "INFO" "Checking home directory for: ${settings.manage_username}" "user_deletion.log"
log "Checking home directory for: ${settings.manage_username}"
if test -d /home/sftpusers/${settings.manage_username}; then
log_message "INFO" "Home directory found: /home/sftpusers/${settings.manage_username}" "user_deletion.log"
log "Home directory found: /home/sftpusers/${settings.manage_username}"
exit 0
else
log_message "ERROR" "Home directory not found for user: ${settings.manage_username}" "user_deletion.log"
log_error "Home directory not found for user: ${settings.manage_username}"
echo "User home directory not found"
exit 1
fi
@ -318,38 +318,38 @@ actions:
user: root
commands: |-
# Source the logging helper
source /home/jelastic/add-sftp-user-addon/log_helper.sh
source /home/jelastic/add-sftp-user-addon/scripts/logging.sh
log_message "INFO" "Starting deletion of user: ${settings.manage_username}" "user_deletion.log"
log "Starting deletion of user: ${settings.manage_username}"
# First unmount any bind mounts
if mount | grep -q "/home/sftpusers/${settings.manage_username}/data/ROOT"; then
log_message "INFO" "Unmounting bind mount for user: ${settings.manage_username}" "user_deletion.log"
log "Unmounting bind mount for user: ${settings.manage_username}"
umount /home/sftpusers/${settings.manage_username}/data/ROOT
fi
# Remove from fstab
if grep -q "/home/sftpusers/${settings.manage_username}/data/ROOT" /etc/fstab; then
log_message "INFO" "Removing bind mount from fstab for user: ${settings.manage_username}" "user_deletion.log"
log "Removing bind mount from fstab for user: ${settings.manage_username}"
sed -i "\|/home/sftpusers/${settings.manage_username}/data/ROOT|d" /etc/fstab
fi
# Delete user account
if userdel ${settings.manage_username}; then
log_message "SUCCESS" "User account deleted: ${settings.manage_username}" "user_deletion.log"
log_success "User account deleted: ${settings.manage_username}"
else
log_message "ERROR" "Failed to delete user account: ${settings.manage_username}" "user_deletion.log"
log_error "Failed to delete user account: ${settings.manage_username}"
exit 1
fi
# Remove home directory
if rm -rf /home/sftpusers/${settings.manage_username}; then
log_message "SUCCESS" "Home directory removed: /home/sftpusers/${settings.manage_username}" "user_deletion.log"
log_success "Home directory removed: /home/sftpusers/${settings.manage_username}"
else
log_message "WARNING" "Failed to remove home directory for: ${settings.manage_username}" "user_deletion.log"
log_warning "Failed to remove home directory for: ${settings.manage_username}"
fi
log_message "SUCCESS" "======== USER DELETION COMPLETED ========" "user_deletion.log"
log_success "======== USER DELETION COMPLETED ========"
exit 0
- if ("${response.exitStatus}" != "0"):
return: deleteUserError
@ -363,24 +363,24 @@ actions:
touch "$LOG_FILE"
# Source the logging helper
source /home/jelastic/add-sftp-user-addon/log_helper.sh
source /home/jelastic/add-sftp-user-addon/scripts/logging.sh
# Log start of process
log_message "INFO" "======== LISTING SFTP USERS ========" "list_users.log"
log_message "INFO" "Retrieving list of SFTP users" "list_users.log"
log "======== LISTING SFTP USERS ========"
log "Retrieving list of SFTP users"
# List users without logging to stdout - only return the clean list
USERS_LIST=$(ls -ld /home/sftpusers/* 2>/dev/null | grep -v "total" | awk '{printf "Username: %s - Created: %s %s %s\n", substr($9, 17), $6, $7, $8}')
# Check if any users were found
if [ -z "$USERS_LIST" ]; then
log_message "WARNING" "No SFTP users found" "list_users.log"
log_message "INFO" "======== USER LISTING COMPLETED ========" "list_users.log"
log_warning "No SFTP users found"
log "======== USER LISTING COMPLETED ========"
echo ""
else
log_message "SUCCESS" "Retrieved list of SFTP users" "list_users.log"
log_message "DEBUG" "Found users: $(echo "$USERS_LIST" | wc -l)" "list_users.log"
log_message "INFO" "======== USER LISTING COMPLETED ========" "list_users.log"
log_success "Retrieved list of SFTP users"
log_debug "Found users: $(echo "$USERS_LIST" | wc -l)"
log "======== USER LISTING COMPLETED ========"
echo "$USERS_LIST"
fi
- if ("${response.exitStatus}" != "0" || "${response.out}" == ""):

131
scripts/logging.sh 100644
View File

@ -0,0 +1,131 @@
#!/bin/bash
# ==============================================================================
# Canonical Logging Library for SFTP User Addon
#
# Provides a standardized set of functions for logging operations, errors,
# debug messages, and command executions. This script is intended to be
# sourced by other scripts in the addon.
# ==============================================================================
# ------------------------------------------------------------------------------
# Log Configuration
# ------------------------------------------------------------------------------
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"
# Enable debug logging by setting DEBUG=1 in the environment, otherwise default to 0.
DEBUG=${DEBUG:-0}
# Ensure the log directory exists. The calling script should have permissions.
mkdir -p "$LOG_DIR" &>/dev/null
# ------------------------------------------------------------------------------
# Core Logging Engine
#
# @param {string} level - The log level (e.g., INFO, ERROR, DEBUG).
# @param {string} message - The message to log.
# ------------------------------------------------------------------------------
log_to_file() {
local level=${1:-INFO}
local message=${2}
local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
# Generate a unique ID for each script execution for better traceability.
local script_id="$(date +%Y%m%d%H%M%S)-$$"
# Write to the main aggregated log file.
echo "[$script_id] $timestamp [$level] $message" >> "$LOG_FILE"
# Route messages to specific logs based on level.
if [[ "$level" == "ERROR" || "$level" == "WARNING" ]]; then
echo "[$script_id] $timestamp [$level] $message" >> "$ERROR_LOG"
fi
if [[ "$level" == "INFO" || "$level" == "SUCCESS" ]]; then
echo "[$script_id] $timestamp [$level] $message" >> "$OPERATION_LOG"
fi
if [[ "$level" == "DEBUG" && "$DEBUG" -eq 1 ]]; then
echo "[$script_id] $timestamp [$level] $message" >> "$DEBUG_LOG"
fi
}
# ------------------------------------------------------------------------------
# Logging Helper Functions
# ------------------------------------------------------------------------------
# Logs an informational message.
log() {
log_to_file "INFO" "$1"
}
# Logs an error message.
log_error() {
log_to_file "ERROR" "$1"
}
# Logs a warning message.
log_warning() {
log_to_file "WARNING" "$1"
}
# Logs a debug message, only if DEBUG is enabled.
log_debug() {
if [ "$DEBUG" -eq 1 ]; then
log_to_file "DEBUG" "$1"
fi
}
# Logs a success message.
log_success() {
log_to_file "SUCCESS" "$1"
}
# ------------------------------------------------------------------------------
# System & Command Logging
# ------------------------------------------------------------------------------
# Logs critical 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}')"
# Note: Specific script parameters should be logged by the calling script.
log_debug "=============================================="
}
# Executes a command, captures its output and status, and logs the result.
#
# @param {string} cmd - The command to execute.
# @param {string} cmd_desc - A human-readable description of the command.
# @returns {int} The exit status of the executed command.
# ------------------------------------------------------------------------------
log_cmd() {
local cmd="$1"
local cmd_desc="$2"
log_debug "Executing: $cmd_desc"
log_debug "Command: $cmd"
# Execute command and capture combined stdout/stderr.
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 with status $status: $cmd_desc"
log_error "Error output: $output"
fi
return $status
}

View File

@ -0,0 +1,106 @@
#!/bin/bash
# ==============================================================================
# Canonical System Preparation Library for SFTP User Addon
#
# Provides a standardized function to prepare the Jelastic environment for
# secure SFTP/SSH user chroot jailing.
# ==============================================================================
# This script expects that 'scripts/logging.sh' has already been sourced
# by the calling script, so functions like log() and log_cmd() are available.
prepare_sftp_system() {
log "Phase: Preparing and Verifying System for SFTP/SSH"
# --------------------------------------------------------------------------
# Step 1: Fix critical directory permissions for chroot
# --------------------------------------------------------------------------
log "Checking and fixing critical directory permissions for chroot jail..."
if [ -d "/home" ] && [[ "$(stat -c '%U:%G' /home)" != "root:root" ]]; then
log "Fixing /home ownership. Current: $(stat -c '%U:%G' /home)"
log_cmd "chown root:root /home" "Set /home owner to root:root"
fi
log_cmd "chmod 755 /home" "Set /home permissions to 755"
log_success "Directory permissions check complete."
# --------------------------------------------------------------------------
# Step 2: Ensure PasswordAuthentication is enabled globally
# --------------------------------------------------------------------------
local sshd_config="/etc/ssh/sshd_config"
log "Ensuring global PasswordAuthentication is enabled in $sshd_config..."
# Use grep to check the current state and sed to change it if needed.
# This is more robust than blindly adding a line.
if grep -qE "^[#\s]*PasswordAuthentication\s+no" "$sshd_config"; then
log "PasswordAuthentication is disabled, enabling it now."
# Comment out the old line and add the new one to be explicit.
log_cmd "sed -i 's/^[#\s]*PasswordAuthentication\s+no/#&/' $sshd_config" "Commenting out old PasswordAuthentication line"
log_cmd "echo 'PasswordAuthentication yes' >> $sshd_config" "Adding 'PasswordAuthentication yes' to sshd_config"
elif ! grep -qE "^[#\s]*PasswordAuthentication\s+yes" "$sshd_config"; then
log "PasswordAuthentication setting not found, adding it."
log_cmd "echo 'PasswordAuthentication yes' >> $sshd_config" "Adding 'PasswordAuthentication yes' to sshd_config"
else
log_debug "PasswordAuthentication is already enabled."
fi
# --------------------------------------------------------------------------
# Step 3: Create a dedicated addon config for modern SSH servers
# --------------------------------------------------------------------------
local addon_config_file="/etc/ssh/sshd_config.d/99-sftp-addon.conf"
log "Creating dedicated SSH configuration at $addon_config_file..."
# This configuration uses a two-group system:
# 1. 'sftpusers': SFTP-only, forced into SFTP mode.
# 2. 'sshusers': SFTP + SSH, allowed a real shell but still chrooted.
cat > "$addon_config_file" << EOF
# Configuration managed by SFTP-Addon - DO NOT EDIT MANUALLY
# --- SFTP-ONLY USERS ---
# Users in this group are forced into the SFTP server and cannot get a shell.
Match Group sftpusers
ChrootDirectory /home/sftpusers/%u
ForceCommand internal-sftp
PasswordAuthentication yes
AllowTcpForwarding no
X11Forwarding no
# --- SSH & SFTP USERS ---
# Users in this group can get a real SSH shell but are jailed to their home.
Match Group sshusers
ChrootDirectory /home/sftpusers/%u
PasswordAuthentication yes
AllowTcpForwarding no
X11Forwarding no
EOF
log_success "Created dedicated SSH configuration for addon."
# --------------------------------------------------------------------------
# Step 4: Validate configuration and restart SSH service
# --------------------------------------------------------------------------
log "Validating new SSH configuration..."
if log_cmd "sshd -t" "Validating sshd configuration syntax"; then
log_success "SSH configuration is valid. Restarting SSH service..."
if ! log_cmd "systemctl restart sshd" "Restarting sshd service"; then
log_error "Failed to restart sshd, but configuration was valid. Manual check may be required."
return 1
fi
log_success "SSHD service restarted successfully."
else
log_error "SSH configuration is INVALID. The new config file at $addon_config_file may contain an error."
# We don't revert here because the base config was likely valid.
# The new file simply won't be loaded by sshd.
return 1
fi
# Ensure /home/sftpusers exists with correct ownership and permissions
if [ ! -d "/home/sftpusers" ]; then
log_cmd "mkdir -p /home/sftpusers" "Creating /home/sftpusers directory"
fi
log_cmd "chown root:root /home/sftpusers" "Setting /home/sftpusers ownership to root:root"
log_cmd "chmod 755 /home/sftpusers" "Setting /home/sftpusers permissions to 755"
log_success "System preparation complete."
return 0
}