--- name: ssh-server-setup description: "Set up SSH key authentication, configure firewalls, harden SSH, and audit server security. Use when: (1) user needs SSH access to a server, (2) SSH connection fails with permission errors, (3) setting up key-based auth for new users, (4) enabling/configuring UFW firewall, (5) analyzing SSH attacks, (6) hardening SSH config." --- # SSH Server Setup & Troubleshooting ## Quick Setup (New Key Pair) ### 1. Generate Key Pair ```bash # On client machine (or use Xshell's key generator) ssh-keygen -t rsa -b 2048 -f ~/.ssh/id_rsa -C "user@host" ``` ### 2. Install Public Key on Server ```bash # On server mkdir -p /home//.ssh echo '' >> /home//.ssh/authorized_keys chmod 700 /home//.ssh chmod 600 /home//.ssh/authorized_keys chmod 755 /home/ # CRITICAL: must NOT be 777 or group-writable chown -R : /home//.ssh ``` ### 3. Verify SSH Config Allows Key Auth ```bash grep -E "^(PubkeyAuthentication|AuthorizedKeysFile)" /etc/ssh/sshd_config # Should show: # PubkeyAuthentication yes # AuthorizedKeysFile .ssh/authorized_keys ``` ## ⚠️ Critical Pitfall: Home Directory Permissions **Symptom**: SSH logs show `Authentication refused: bad ownership or modes for directory /home/` **Root cause**: SSH (OpenSSH) refuses public key authentication if the user's home directory has group or other write permissions (e.g., 777, 775). **Fix**: ```bash chmod 755 /home/ ``` **Why**: OpenSSH considers a writable home directory a security risk — other users could manipulate `~/.ssh/authorized_keys`. The directory must be owned by the user and not writable by group/others. **Debugging**: ```bash # Check current permissions ls -la /home/ | grep # Should show drwxr-xr-x (755), NOT drwxrwxrwx (777) or drwxrwxr-x (775) # Check SSH logs for the exact error tail -20 /var/log/auth.log | grep -i "ssh\|publickey" # Or on systemd systems: journalctl -u ssh --no-pager -n 20 ``` ## Permission Checklist | Path | Owner | Permissions | Why | |------|-------|-------------|-----| | `/home/` | `` | `755` | SSH refuses auth if group/other writable | | `~/.ssh/` | `` | `700` | Only owner should access SSH config | | `~/.ssh/authorized_keys` | `` | `600` | Only owner should read/write keys | | `~/.ssh/id_rsa` (private) | `` | `600` | Private key must be restricted | ## Xshell-Specific Notes 1. **Generate key**: 工具 → 用户密钥管理器 → 新建 → RSA 2048 2. **Import key**: 工具 → 用户密钥管理器 → 导入 3. **Connection settings**: - 协议: SSH - 用户身份验证: 方法选 **Public Key**(不是 Password) - 用户名: `ubuntu` (or whatever the server user is) - 用户密钥: select the imported key ## Common Errors | Error | Cause | Fix | |-------|-------|-----| | `bad ownership or modes for directory` | Home dir writable by group/others | `chmod 755 /home/` | | `bad ownership or modes for file` | authorized_keys wrong perms | `chmod 600 ~/.ssh/authorized_keys` | | `Permission denied (publickey)` | Key not in authorized_keys | Add public key to file | | `Connection closed by foreign host` | Auth failed, server disconnects | Check logs for specific reason | | `所选的用户密钥未在远程主机上注册` | Public key not installed on server | Add public key to authorized_keys | --- ## UFW Firewall Setup When enabling SSH access, always set up UFW in this order to avoid lockout: ```bash # 1. Allow SSH FIRST (before enabling firewall) sudo ufw allow 22/tcp comment "SSH" # 2. Set default policies sudo ufw default deny incoming sudo ufw default allow outgoing # 3. Enable (use --force for non-interactive) sudo ufw --force enable # 4. Verify sudo ufw status verbose ``` **Opening additional ports later:** ```bash sudo ufw allow 80/tcp comment "HTTP" sudo ufw allow 443/tcp comment "HTTPS" sudo ufw allow from to any port 22 # Restrict SSH to specific IP ``` --- ## SSH Attack Analysis **Check attack patterns:** ```bash # Failed/disconnected attempts with IP counts journalctl -u ssh --no-pager --since "2026-05-01" | \ grep -i "failed\|invalid\|refused\|disconnected.*preauth" | \ grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | sort | uniq -c | sort -rn | head -15 # Successful logins only journalctl -u ssh --no-pager --since "2026-05-01" | grep "Accepted" | \ grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | sort | uniq -c | sort -rn ``` **IP geolocation lookup:** ```bash # ip-api.com (free, no key needed, rate limited 45/min) curl -s "http://ip-api.com/json/?fields=country,regionName,isp,org" ``` **Typical attack sources:** Cloud provider IPs (Tencent Cloud, Alibaba Cloud, OVH, Hetzner) — these are botnet/scanner nodes, not targeted attacks. --- ## SSH Hardening (sshd_config) Add to `/etc/ssh/sshd_config`: ```bash MaxAuthTries 3 # Limit auth attempts per connection LoginGraceTime 30 # Timeout for auth (seconds) ClientAliveInterval 300 # Send keepalive every 5 min ClientAliveCountMax 2 # Disconnect after 2 missed keepalives MaxSessions 3 # Limit concurrent sessions per connection AllowAgentForwarding no # Disable unless needed AllowTcpForwarding no # Disable unless needed ``` Apply: `sudo systemctl restart sshd` --- ## Server Security Audit Checklist ```bash # 1. SSH config cat /etc/ssh/sshd_config | grep -v "^#" | grep -v "^$" # 2. Firewall status sudo ufw status verbose # 3. fail2ban status (if installed) sudo fail2ban-client status sshd # 4. Auto-updates cat /etc/apt/apt.conf.d/20auto-upgrades # 5. Listening ports ss -tlnp | grep -v "127.0.0.1" | grep -v "::1" # 6. System resources free -h && df -h / && uptime # 7. Swap config swapon --show cat /proc/sys/vm/swappiness ```