Files
agent-skills/devops/ssh-server-setup/SKILL.md
Hermes Agent ccc63d1e70 first commit
2026-05-10 13:52:46 +08:00

5.7 KiB
Raw Blame History

name, description
name description
ssh-server-setup 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

# 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

# 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

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:

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:

# 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:

# 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:

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:

# 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:

# 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:

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

# 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