#!/bin/bash # Set debug mode based on the 4th script argument. # This must be set BEFORE sourcing the logging library. export DEBUG=${4:-0} # Source the canonical logging library. # The path is relative to this script's location. source "$(dirname "$0")/scripts/logging.sh" # Source the canonical system preparation library. source "$(dirname "$0")/scripts/system_prep.sh" validate_username() { local username=$1 log_debug "Validating username: $username" 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." return 1 fi log_debug "Username validation passed" return 0 } # Main script USERNAME=$1 PASSWORD=$2 SSH_ENABLED=${3:-false} # Validate required parameters if [ -z "$USERNAME" ] || [ -z "$PASSWORD" ]; then echo "ERROR: Missing required parameters. Usage: $0 [ssh_enabled]" >&2 exit 1 fi # Log to file only log "======== STARTING SFTP USER SETUP ========" log "Script started with username: $USERNAME, ssh_enabled: $SSH_ENABLED" # Pre-flight checks log "Phase 0: Pre-flight environment checks" if [ ! -d "/var/www/webroot/ROOT" ]; then log_error "Web root directory /var/www/webroot/ROOT does not exist" echo "ERROR: Web root directory /var/www/webroot/ROOT does not exist" >&2 exit 1 fi if [ ! -x "/usr/sbin/useradd" ]; then log_error "useradd command not found or not executable" echo "ERROR: useradd command not found or not executable" >&2 exit 1 fi if [ ! -x "/usr/sbin/chpasswd" ]; then log_error "chpasswd command not found or not executable" echo "ERROR: chpasswd command not found or not executable" >&2 exit 1 fi 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 ! 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" # Validate username format log "Phase 2: Validating username" if ! validate_username "$USERNAME"; then log_error "Username validation failed, exiting" echo "ERROR: Username validation failed for: $USERNAME" >&2 exit 1 fi log_success "Username validation passed" # Check if user already exists log "Phase 3: Checking if user already exists" if id "$USERNAME" &>/dev/null; then log_error "Username $USERNAME already exists. Please choose a different username." echo "ERROR: Username $USERNAME already exists" >&2 exit 1 fi log_success "Username is available for creation" USER_HOME="/home/sftpusers/$USERNAME" ROOT_DIRECTORY="/var/www/webroot/ROOT" log_debug "Setting paths - USER_HOME: $USER_HOME, ROOT_DIRECTORY: $ROOT_DIRECTORY" # 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-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 log "Phase 5: Setting up directories" if [ ! -d "/home/sftpusers" ]; then 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" fi log_success "Directory setup completed" # Determine shell path if [ "$SSH_ENABLED" = "true" ]; then USER_SHELL="/bin/bash" else USER_SHELL="/sbin/nologin" fi # Create the user account log "Phase 6: Creating user account" if [ "$SSH_ENABLED" = "true" ]; then log "Creating user with SSH access" if ! log_cmd "useradd -d $USER_HOME -m -s $USER_SHELL $USERNAME" "Creating user with SSH shell inside jail"; then log_error "Failed to create user account with SSH access" echo "ERROR: Failed to create user account with SSH access" >&2 exit 1 fi else log "Creating user with SFTP-only access" if ! log_cmd "useradd -d $USER_HOME -m -s $USER_SHELL $USERNAME" "Creating user with nologin shell"; then log_error "Failed to create user account with SFTP-only access" echo "ERROR: Failed to create user account with SFTP-only access" >&2 exit 1 fi fi log_success "User account created" # Set password log "Phase 7: Setting user password" # Set password directly - chpasswd reads username:password from stdin # Use printf to handle special characters in password securely CHPASSWD_OUTPUT=$(printf '%s:%s\n' "$USERNAME" "$PASSWORD" | chpasswd 2>&1) CHPASSWD_STATUS=$? if [ -n "$CHPASSWD_OUTPUT" ]; then log_debug "chpasswd output: $CHPASSWD_OUTPUT" fi if [ $CHPASSWD_STATUS -eq 0 ]; then log_success "Password set for user $USERNAME" else log_error "Failed to set password for user $USERNAME" echo "ERROR: Failed to set password for user $USERNAME" >&2 exit 1 fi # 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" # 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" # Create mount point for webroot (using bind mount instead of symlink) log "Phase 9: Setting up webroot access via bind mount" if ! log_cmd "mkdir -p $USER_HOME/data/ROOT" "Creating ROOT mount point"; then log_error "Failed to create ROOT mount point directory" echo "ERROR: Failed to create ROOT mount point directory" >&2 exit 1 fi # Check if ROOT_DIRECTORY exists before mounting if [ ! -d "$ROOT_DIRECTORY" ]; then log_error "Root directory $ROOT_DIRECTORY does not exist" echo "ERROR: Root directory $ROOT_DIRECTORY does not exist" >&2 exit 1 fi if ! log_cmd "mount --bind $ROOT_DIRECTORY $USER_HOME/data/ROOT" "Binding webroot to user's ROOT directory"; then log_error "Failed to create bind mount for webroot access" echo "ERROR: Failed to create bind mount for webroot access" >&2 exit 1 fi # Add mount to fstab to persist across reboots if ! grep -q "$ROOT_DIRECTORY $USER_HOME/data/ROOT" /etc/fstab; then if ! log_cmd "echo \"$ROOT_DIRECTORY $USER_HOME/data/ROOT none bind 0 0\" >> /etc/fstab" "Adding bind mount to fstab"; then log_warning "Failed to add bind mount to fstab - mount may not persist across reboots" fi fi log_success "Created bind mount for webroot access" # No additional mounts needed for SSH users (no chroot) # Add user to the required groups log "Phase 10: Adding user to groups" 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 for file access" log_success "Added $USERNAME to litespeed group for file access" # Ensure webroot has correct group ownership and permissions for write access log "Phase 10b: Verifying webroot permissions for group write access" if [ -d "$ROOT_DIRECTORY" ]; then # Check if litespeed group exists if getent group litespeed > /dev/null; then # Check current group ownership CURRENT_GROUP=$(stat -c '%G' "$ROOT_DIRECTORY" 2>/dev/null) if [ "$CURRENT_GROUP" != "litespeed" ]; then log "Setting webroot group ownership to litespeed (was: $CURRENT_GROUP)" log_cmd "chgrp -R litespeed $ROOT_DIRECTORY" "Setting webroot group to litespeed" fi # Check if group write permission exists # Group write exists if second digit is 7(rwx), 6(rw-), 3(-wx), or 2(-w-) CURRENT_PERMS=$(stat -c '%a' "$ROOT_DIRECTORY" 2>/dev/null) if [ -n "$CURRENT_PERMS" ]; then # Extract group write bit (second digit) GROUP_WRITE_BIT=$(echo "$CURRENT_PERMS" | cut -c2) # Check if write bit is NOT set (4=r--, 5=r-x, 1=--x, 0=---) if [ "$GROUP_WRITE_BIT" = "4" ] || [ "$GROUP_WRITE_BIT" = "5" ] || [ "$GROUP_WRITE_BIT" = "1" ] || [ "$GROUP_WRITE_BIT" = "0" ]; then log "Adding group write permissions to webroot (current: $CURRENT_PERMS)" log_cmd "chmod -R g+w $ROOT_DIRECTORY" "Adding group write permissions to webroot" else log_debug "Webroot already has group write permissions (current: $CURRENT_PERMS)" fi fi else log_warning "litespeed group does not exist, skipping webroot permission setup" fi else log_warning "Webroot directory $ROOT_DIRECTORY does not exist, skipping permission check" fi # Create welcome file log "Phase 11: Creating welcome file" cat > $USER_HOME/data/welcome.txt << EOF Welcome to your SFTP account on Jelastic! Your account has been set up with the following details: Username: $USERNAME Home Directory: $USER_HOME Web Root: $USER_HOME/data/ROOT (symlink to $ROOT_DIRECTORY) 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" # Always export variables directly with no console output export CREATED_USERNAME="$USERNAME" export CREATED_PASSWORD="$PASSWORD" # All logging should be to file only log_success "Script completed successfully for user $USERNAME" log "======== SFTP USER SETUP COMPLETE ========" exit 0