diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md new file mode 100644 index 0000000..174ec13 --- /dev/null +++ b/TROUBLESHOOTING.md @@ -0,0 +1,250 @@ +# SFTP/SSH Account Troubleshooting Guide + +## Critical Bug Fixed + +### Password Setting Bug (CRITICAL) +**Location**: `add-sftp.sh` line 150 +**Issue**: The password was being set using single quotes which prevented variable expansion when passed through `log_cmd` function (which uses `eval`). + +**Original Code (BROKEN)**: +```bash +log_cmd "echo '$USERNAME:$PASSWORD' | chpasswd" "Setting user password" +``` + +**Fixed Code**: +```bash +CHPASSWD_OUTPUT=$(printf '%s:%s\n' "$USERNAME" "$PASSWORD" | chpasswd 2>&1) +CHPASSWD_STATUS=$? +if [ $CHPASSWD_STATUS -eq 0 ]; then + log_success "Password set for user $USERNAME" +else + log_error "Failed to set password for user $USERNAME" + exit 1 +fi +``` + +**Impact**: This bug would cause passwords to NOT be set properly, resulting in login failures even if the account was created successfully. + +--- + +## Server Diagnostic Commands + +Run these commands on your server as **root** to diagnose the issue with user `mightyuser1`: + +### 1. Check if User Exists +```bash +id mightyuser1 +getent passwd mightyuser1 +``` + +### 2. Check User Groups +```bash +id -Gn mightyuser1 +``` +**Expected**: User should be in either `sftpusers` (SFTP-only) or `sshusers` (SSH+SFTP), and `litespeed` group. + +### 3. Check User Home Directory +```bash +getent passwd mightyuser1 | cut -d: -f6 +ls -ld /home/sftpusers/mightyuser1 +stat /home/sftpusers/mightyuser1 +``` +**Expected**: +- Home directory should exist at `/home/sftpusers/mightyuser1` +- Ownership should be `root:root` +- Permissions should be `755` or `751` + +### 4. Check Password Status +```bash +grep "^mightyuser1:" /etc/shadow | cut -d: -f2 +``` +**Expected**: Should show a hashed password (long string starting with `$`). If it shows `*`, `!`, or is empty, the password is NOT set. + +### 5. Check SSH Configuration +```bash +# Check if password authentication is enabled +grep -E "^PasswordAuthentication" /etc/ssh/sshd_config + +# Check Match Group configuration +grep -A 5 "Match Group sftpusers" /etc/ssh/sshd_config +grep -A 5 "Match Group sshusers" /etc/ssh/sshd_config + +# Check addon config file +cat /etc/ssh/sshd_config.d/99-sftp-addon.conf 2>/dev/null + +# Test SSH config syntax +sshd -t +``` + +### 6. Check SSH Service Status +```bash +systemctl status sshd +systemctl is-active sshd +``` + +### 7. Check User Creation Logs +```bash +# Find the most recent user creation log +ls -t /opt/add-sftp-user-addon/logs/user_creation-*.log | head -1 | xargs cat + +# Check error logs +tail -20 /opt/add-sftp-user-addon/logs/errors.log + +# Check main log +tail -50 /opt/add-sftp-user-addon/logs/script_output.log +``` + +### 8. Check Directory Structure +```bash +# Check if data directory exists and has correct permissions +ls -ld /home/sftpusers/mightyuser1/data +ls -ld /home/sftpusers/mightyuser1/data/ROOT + +# Check if ROOT is mounted +mountpoint /home/sftpusers/mightyuser1/data/ROOT +mount | grep "mightyuser1" +``` + +### 9. Check User Shell +```bash +getent passwd mightyuser1 | cut -d: -f7 +``` +**Expected**: +- `/sbin/nologin` for SFTP-only users (should be in `sftpusers` group) +- `/bin/bash` for SSH+SFTP users (should be in `sshusers` group) + +--- + +## Quick Fixes (Run as Root) + +### Fix 1: Reset Password +```bash +# Replace NEW_PASSWORD with the actual password +echo "mightyuser1:NEW_PASSWORD" | chpasswd + +# Verify password was set +grep "^mightyuser1:" /etc/shadow | cut -d: -f2 +``` + +### Fix 2: Fix Group Membership +```bash +# For SFTP-only access: +usermod -aG sftpusers,litespeed mightyuser1 +usermod -s /sbin/nologin mightyuser1 + +# For SSH+SFTP access: +usermod -aG sshusers,litespeed mightyuser1 +usermod -s /bin/bash mightyuser1 + +# Verify +id -Gn mightyuser1 +``` + +### Fix 3: Fix Home Directory Permissions +```bash +chown root:root /home/sftpusers/mightyuser1 +chmod 755 /home/sftpusers/mightyuser1 +chown mightyuser1:mightyuser1 /home/sftpusers/mightyuser1/data +chmod 775 /home/sftpusers/mightyuser1/data +``` + +### Fix 4: Remount ROOT Directory +```bash +# Unmount if already mounted +umount /home/sftpusers/mightyuser1/data/ROOT 2>/dev/null + +# Remount +mount --bind /var/www/webroot/ROOT /home/sftpusers/mightyuser1/data/ROOT + +# Verify +mountpoint /home/sftpusers/mightyuser1/data/ROOT +``` + +### Fix 5: Enable Password Authentication (if disabled) +```bash +# Check current setting +grep "^PasswordAuthentication" /etc/ssh/sshd_config + +# Enable if disabled +sed -i 's/^PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_config + +# Restart SSH service +systemctl restart sshd +``` + +--- + +## Code Analysis + +### Algorithm Review + +The account creation process follows these phases: + +1. **Pre-flight Checks**: Validates environment and required directories exist +2. **System Preparation**: Configures SSH/SFTP service and ensures password auth is enabled +3. **Username Validation**: Validates username format (3-32 chars, alphanumeric + underscore) +4. **User Existence Check**: Prevents duplicate users +5. **Group Setup**: Creates `sftpusers` and `sshusers` groups if needed +6. **Directory Setup**: Creates `/home/sftpusers` with proper permissions +7. **User Creation**: Creates user with appropriate shell (`/bin/bash` for SSH, `/sbin/nologin` for SFTP-only) +8. **Password Setting**: **FIXED** - Now uses `printf` to properly set password +9. **Chroot Setup**: Sets up chroot jail structure with proper ownership +10. **Bind Mount**: Creates bind mount for webroot access +11. **Group Assignment**: Adds user to appropriate groups (`sftpusers` or `sshusers` + `litespeed`) + +### Potential Issues Identified + +1. **FIXED**: Password setting bug (single quotes preventing variable expansion) +2. **Configuration Duplication**: Both `manifest.jps` and `system_prep.sh` configure SSH, which could cause conflicts +3. **Password Special Characters**: The fix uses `printf` which handles special characters better than `echo` +4. **SSH Config File Location**: The code creates config in `/etc/ssh/sshd_config.d/99-sftp-addon.conf` but also modifies main `/etc/ssh/sshd_config` - ensure Include directive exists + +### Recommendations + +1. **Deploy the fix** - The password setting bug is critical and must be fixed +2. **Test password reset** - For existing users created with the bug, reset their passwords +3. **Verify SSH config** - Ensure `/etc/ssh/sshd_config` includes `Include /etc/ssh/sshd_config.d/*.conf` +4. **Check logs** - Review user creation logs to see if password setting failed silently + +--- + +## Using the Diagnostic Script + +A comprehensive diagnostic script `check-sftp-user.sh` has been created. Upload it to your server and run: + +```bash +chmod +x check-sftp-user.sh +./check-sftp-user.sh mightyuser1 +``` + +This script will check all aspects of the user account and provide detailed diagnostics. + +--- + +## Common Issues and Solutions + +### Issue: "Permission denied" when logging in +**Causes**: +1. Password not set (most likely due to the bug) +2. User in wrong group +3. Password authentication disabled in SSH config +4. Home directory permissions incorrect + +**Solution**: Run diagnostic commands above, then apply appropriate fixes. + +### Issue: User can connect but cannot access files +**Causes**: +1. User not in `litespeed` group +2. ROOT directory not mounted +3. Data directory permissions incorrect + +**Solution**: Check group membership and mount status, fix as needed. + +### Issue: SSH works but SFTP doesn't (or vice versa) +**Causes**: +1. User in wrong group (`sftpusers` vs `sshusers`) +2. Shell mismatch (`/sbin/nologin` vs `/bin/bash`) +3. SSH config Match Group rules incorrect + +**Solution**: Verify group membership and shell match the desired access type. + diff --git a/add-sftp.sh b/add-sftp.sh index 901bc1d..f53c8d3 100644 --- a/add-sftp.sh +++ b/add-sftp.sh @@ -147,12 +147,20 @@ log_success "User account created" # Set password log "Phase 7: Setting user password" -if ! log_cmd "echo '$USERNAME:$PASSWORD' | chpasswd" "Setting user password"; then +# 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 -log_success "Password set for user $USERNAME" # Set up proper directory structure for chroot jail log "Phase 8: Setting up chroot jail structure" diff --git a/check-sftp-user.sh b/check-sftp-user.sh new file mode 100644 index 0000000..897b239 --- /dev/null +++ b/check-sftp-user.sh @@ -0,0 +1,314 @@ +#!/bin/bash + +# ============================================================================== +# SFTP User Diagnostic Script +# Use this script to diagnose SFTP/SSH account creation issues +# ============================================================================== + +USERNAME="${1:-mightyuser1}" + +echo "==============================================================================" +echo "SFTP/SSH User Diagnostic Script" +echo "Checking user: $USERNAME" +echo "Timestamp: $(date)" +echo "==============================================================================" +echo + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Function to print colored output +print_status() { + local status=$1 + local message=$2 + if [ "$status" = "OK" ]; then + echo -e "${GREEN}[OK]${NC} $message" + elif [ "$status" = "ERROR" ]; then + echo -e "${RED}[ERROR]${NC} $message" + elif [ "$status" = "WARNING" ]; then + echo -e "${YELLOW}[WARNING]${NC} $message" + else + echo "[INFO] $message" + fi +} + +echo "=== 1. USER ACCOUNT CHECK ===" +if id "$USERNAME" &>/dev/null; then + print_status "OK" "User account exists: $USERNAME" + echo " User ID: $(id -u $USERNAME)" + echo " Group ID: $(id -g $USERNAME)" + echo " Groups: $(id -Gn $USERNAME)" + echo " Home Directory: $(getent passwd $USERNAME | cut -d: -f6)" + echo " Shell: $(getent passwd $USERNAME | cut -d: -f7)" +else + print_status "ERROR" "User account does NOT exist: $USERNAME" + echo " Run: id $USERNAME" +fi +echo + +echo "=== 2. USER GROUPS CHECK ===" +if id "$USERNAME" &>/dev/null; then + GROUPS=$(id -Gn "$USERNAME") + if echo "$GROUPS" | grep -q "sftpusers"; then + print_status "OK" "User is in sftpusers group (SFTP-only access)" + elif echo "$GROUPS" | grep -q "sshusers"; then + print_status "OK" "User is in sshusers group (SSH+SFTP access)" + else + print_status "ERROR" "User is NOT in sftpusers or sshusers group!" + echo " Current groups: $GROUPS" + echo " Fix: usermod -aG sftpusers $USERNAME (for SFTP-only)" + echo " Fix: usermod -aG sshusers $USERNAME (for SSH+SFTP)" + fi + + if echo "$GROUPS" | grep -q "litespeed"; then + print_status "OK" "User is in litespeed group" + else + print_status "WARNING" "User is NOT in litespeed group (may affect file access)" + fi +fi +echo + +echo "=== 3. HOME DIRECTORY CHECK ===" +if id "$USERNAME" &>/dev/null; then + USER_HOME=$(getent passwd $USERNAME | cut -d: -f6) + if [ -d "$USER_HOME" ]; then + print_status "OK" "Home directory exists: $USER_HOME" + echo " Ownership: $(stat -c '%U:%G' $USER_HOME)" + echo " Permissions: $(stat -c '%a' $USER_HOME)" + + # Check if home directory is owned by root (required for chroot) + if [ "$(stat -c '%U' $USER_HOME)" = "root" ]; then + print_status "OK" "Home directory is owned by root (required for chroot)" + else + print_status "ERROR" "Home directory must be owned by root for chroot!" + echo " Fix: chown root:root $USER_HOME" + fi + + # Check permissions + PERMS=$(stat -c '%a' $USER_HOME) + if [ "$PERMS" = "755" ] || [ "$PERMS" = "751" ]; then + print_status "OK" "Home directory permissions are correct: $PERMS" + else + print_status "WARNING" "Home directory permissions should be 755 or 751, current: $PERMS" + echo " Fix: chmod 755 $USER_HOME" + fi + else + print_status "ERROR" "Home directory does NOT exist: $USER_HOME" + fi +fi +echo + +echo "=== 4. DIRECTORY STRUCTURE CHECK ===" +if id "$USERNAME" &>/dev/null; then + USER_HOME=$(getent passwd $USERNAME | cut -d: -f6) + if [ -d "$USER_HOME/data" ]; then + print_status "OK" "Data directory exists: $USER_HOME/data" + echo " Ownership: $(stat -c '%U:%G' $USER_HOME/data)" + echo " Permissions: $(stat -c '%a' $USER_HOME/data)" + + if [ -d "$USER_HOME/data/ROOT" ]; then + print_status "OK" "ROOT directory exists: $USER_HOME/data/ROOT" + # Check if it's a mount point + if mountpoint -q "$USER_HOME/data/ROOT" 2>/dev/null; then + print_status "OK" "ROOT is properly mounted (bind mount)" + echo " Mount info: $(mount | grep "$USER_HOME/data/ROOT")" + else + print_status "WARNING" "ROOT directory exists but is not mounted" + echo " Fix: mount --bind /var/www/webroot/ROOT $USER_HOME/data/ROOT" + fi + else + print_status "ERROR" "ROOT directory does NOT exist: $USER_HOME/data/ROOT" + fi + else + print_status "ERROR" "Data directory does NOT exist: $USER_HOME/data" + fi +fi +echo + +echo "=== 5. PASSWORD CHECK ===" +if id "$USERNAME" &>/dev/null; then + # Check if password is set (this is tricky - we can only check if shadow entry exists) + if grep -q "^$USERNAME:" /etc/shadow; then + print_status "OK" "User has shadow entry (password record exists)" + # Check if password field is empty or has '!' (locked) + PWD_FIELD=$(grep "^$USERNAME:" /etc/shadow | cut -d: -f2) + if [ -z "$PWD_FIELD" ] || [ "$PWD_FIELD" = "*" ] || [ "$PWD_FIELD" = "!" ]; then + print_status "ERROR" "User password appears to be LOCKED or NOT SET!" + echo " Password field: $PWD_FIELD" + echo " Fix: echo '$USERNAME:NEW_PASSWORD' | chpasswd" + else + print_status "OK" "User password appears to be set (hashed)" + fi + else + print_status "ERROR" "User does NOT have shadow entry!" + fi +fi +echo + +echo "=== 6. SSH CONFIGURATION CHECK ===" +echo "Checking SSH daemon configuration..." + +# Check if sshd_config.d directory exists and is included +if [ -d "/etc/ssh/sshd_config.d" ]; then + print_status "OK" "SSH config.d directory exists" + if grep -q "^Include" /etc/ssh/sshd_config | grep -q "sshd_config.d"; then + print_status "OK" "sshd_config.d is included in main config" + else + print_status "WARNING" "sshd_config.d may not be included (check Include directive)" + fi +else + print_status "WARNING" "sshd_config.d directory does not exist" +fi + +# Check addon config file +ADDON_CONFIG="/etc/ssh/sshd_config.d/99-sftp-addon.conf" +if [ -f "$ADDON_CONFIG" ]; then + print_status "OK" "Addon config file exists: $ADDON_CONFIG" + echo " Contents:" + cat "$ADDON_CONFIG" | sed 's/^/ /' +else + print_status "WARNING" "Addon config file does NOT exist: $ADDON_CONFIG" +fi + +# Check main sshd_config +if grep -qE "^PasswordAuthentication\s+yes" /etc/ssh/sshd_config; then + print_status "OK" "PasswordAuthentication is enabled in main config" +elif grep -qE "^PasswordAuthentication\s+no" /etc/ssh/sshd_config; then + print_status "ERROR" "PasswordAuthentication is DISABLED in main config!" + echo " Fix: sed -i 's/^PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_config" +else + print_status "WARNING" "PasswordAuthentication setting not found in main config (may use default)" +fi + +# Check Match Group configuration +if grep -q "Match Group sftpusers" /etc/ssh/sshd_config; then + print_status "OK" "Match Group sftpusers found in main config" +elif [ -f "$ADDON_CONFIG" ] && grep -q "Match Group sftpusers" "$ADDON_CONFIG"; then + print_status "OK" "Match Group sftpusers found in addon config" +else + print_status "ERROR" "Match Group sftpusers NOT found in SSH config!" +fi + +if grep -q "Match Group sshusers" /etc/ssh/sshd_config; then + print_status "OK" "Match Group sshusers found in main config" +elif [ -f "$ADDON_CONFIG" ] && grep -q "Match Group sshusers" "$ADDON_CONFIG"; then + print_status "OK" "Match Group sshusers found in addon config" +else + print_status "WARNING" "Match Group sshusers NOT found (needed for SSH access)" +fi + +# Test SSH config syntax +if sshd -t 2>/dev/null; then + print_status "OK" "SSH configuration syntax is valid" +else + print_status "ERROR" "SSH configuration syntax is INVALID!" + echo " Run: sshd -t (as root) to see errors" +fi +echo + +echo "=== 7. SSH SERVICE STATUS ===" +if systemctl is-active --quiet sshd; then + print_status "OK" "SSH service is running" +else + print_status "ERROR" "SSH service is NOT running!" + echo " Fix: systemctl start sshd" +fi + +if systemctl is-enabled --quiet sshd; then + print_status "OK" "SSH service is enabled (will start on boot)" +else + print_status "WARNING" "SSH service is NOT enabled" +fi +echo + +echo "=== 8. RECENT LOGS CHECK ===" +LOG_DIR="/opt/add-sftp-user-addon/logs" +if [ -d "$LOG_DIR" ]; then + print_status "OK" "Log directory exists: $LOG_DIR" + + # Find user creation log + USER_LOG=$(ls -t $LOG_DIR/user_creation-*.log 2>/dev/null | head -1) + if [ -n "$USER_LOG" ]; then + echo " Most recent user creation log: $USER_LOG" + echo " Last 10 lines:" + tail -10 "$USER_LOG" | sed 's/^/ /' + else + print_status "WARNING" "No user creation logs found" + fi + + # Check error log + if [ -f "$LOG_DIR/errors.log" ]; then + ERROR_COUNT=$(wc -l < "$LOG_DIR/errors.log") + if [ "$ERROR_COUNT" -gt 0 ]; then + print_status "WARNING" "Found $ERROR_COUNT lines in error log" + echo " Last 5 errors:" + tail -5 "$LOG_DIR/errors.log" | sed 's/^/ /' + fi + fi +else + print_status "WARNING" "Log directory does not exist: $LOG_DIR" +fi +echo + +echo "=== 9. AUTHENTICATION TEST (SIMULATED) ===" +if id "$USERNAME" &>/dev/null; then + USER_SHELL=$(getent passwd $USERNAME | cut -d: -f7) + if [ "$USER_SHELL" = "/sbin/nologin" ]; then + GROUPS=$(id -Gn "$USERNAME") + if echo "$GROUPS" | grep -q "sftpusers"; then + print_status "OK" "User configured for SFTP-only (nologin shell + sftpusers group)" + echo " Note: This user can ONLY use SFTP, not SSH shell access" + else + print_status "ERROR" "User has nologin shell but NOT in sftpusers group!" + fi + elif [ "$USER_SHELL" = "/bin/bash" ] || [ "$USER_SHELL" = "/bin/sh" ]; then + GROUPS=$(id -Gn "$USERNAME") + if echo "$GROUPS" | grep -q "sshusers"; then + print_status "OK" "User configured for SSH+SFTP (bash shell + sshusers group)" + else + print_status "ERROR" "User has bash shell but NOT in sshusers group!" + echo " Fix: usermod -aG sshusers $USERNAME" + fi + else + print_status "WARNING" "Unexpected shell: $USER_SHELL" + fi +fi +echo + +echo "=== 10. QUICK FIXES ===" +echo "If user exists but login fails, try these commands (as root):" +echo +if id "$USERNAME" &>/dev/null; then + echo "1. Reset password:" + echo " echo '$USERNAME:NEW_PASSWORD' | chpasswd" + echo + echo "2. Fix group membership (choose one based on desired access):" + echo " # For SFTP-only:" + echo " usermod -aG sftpusers,litespeed $USERNAME" + echo " usermod -s /sbin/nologin $USERNAME" + echo " # For SSH+SFTP:" + echo " usermod -aG sshusers,litespeed $USERNAME" + echo " usermod -s /bin/bash $USERNAME" + echo + echo "3. Fix home directory permissions:" + USER_HOME=$(getent passwd $USERNAME | cut -d: -f6) + echo " chown root:root $USER_HOME" + echo " chmod 755 $USER_HOME" + echo + echo "4. Remount ROOT directory:" + echo " mount --bind /var/www/webroot/ROOT $USER_HOME/data/ROOT" + echo + echo "5. Restart SSH service:" + echo " systemctl restart sshd" +else + echo "User does not exist. Cannot provide fixes." +fi +echo + +echo "==============================================================================" +echo "Diagnostic complete!" +echo "==============================================================================" +