diff --git a/add-sftp.sh b/add-sftp.sh index 0b1e0f2..969bd93 100644 --- a/add-sftp.sh +++ b/add-sftp.sh @@ -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 diff --git a/log_helper.sh b/log_helper.sh deleted file mode 100644 index 7a2f79d..0000000 --- a/log_helper.sh +++ /dev/null @@ -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 -} \ No newline at end of file diff --git a/manifest.jps b/manifest.jps index ab6dc14..ac7b88f 100644 --- a/manifest.jps +++ b/manifest.jps @@ -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}" == ""): diff --git a/scripts/logging.sh b/scripts/logging.sh new file mode 100644 index 0000000..9c582d0 --- /dev/null +++ b/scripts/logging.sh @@ -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 +} \ No newline at end of file diff --git a/scripts/system_prep.sh b/scripts/system_prep.sh new file mode 100644 index 0000000..b4da313 --- /dev/null +++ b/scripts/system_prep.sh @@ -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 +} \ No newline at end of file