186 lines
5.7 KiB
Markdown
186 lines
5.7 KiB
Markdown
---
|
||
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
|
||
```
|