first commit
This commit is contained in:
185
devops/ssh-server-setup/SKILL.md
Normal file
185
devops/ssh-server-setup/SKILL.md
Normal file
@@ -0,0 +1,185 @@
|
||||
---
|
||||
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
|
||||
```
|
||||
Reference in New Issue
Block a user