Fix critical password setting bug in add-sftp.sh and add diagnostic tools

main
Anthony 2025-11-04 18:04:57 +08:00
parent 594561da1f
commit af10f82ff0
3 changed files with 574 additions and 2 deletions

250
TROUBLESHOOTING.md 100644
View File

@ -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.

View File

@ -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"

314
check-sftp-user.sh 100644
View File

@ -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 "=============================================================================="