---
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/<user>/.ssh
echo '<public-key-content>' >> /home/<user>/.ssh/authorized_keys
chmod 700 /home/<user>/.ssh
chmod 600 /home/<user>/.ssh/authorized_keys
chmod 755 /home/<user>       # CRITICAL: must NOT be 777 or group-writable
chown -R <user>:<user> /home/<user>/.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/<user>`

**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/<user>
```

**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 <user>
# 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/<user>` | `<user>` | `755` | SSH refuses auth if group/other writable |
| `~/.ssh/` | `<user>` | `700` | Only owner should access SSH config |
| `~/.ssh/authorized_keys` | `<user>` | `600` | Only owner should read/write keys |
| `~/.ssh/id_rsa` (private) | `<user>` | `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/<user>` |
| `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 <specific-ip> 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/<IP>?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
```
