服务器接入公网就会面临暴力破解。用以下命令查看 SSH 登录失败记录:
sudo journalctl -u sshd | grep -i "failed" | tail -30
大量来自陌生 IP 的失败尝试,多数在尝试 root 用户。
本地生成密钥对:
ssh-keygen -t ed25519 -C "你的名字-服务器加固"
查看公钥:
# Windows:
type $env:USERPROFILE\.ssh\id_ed25519.pub
# Mac/Linux:
cat ~/.ssh/id_ed25519.pub
在服务器上安装公钥(study 用户):
sudo su - study
mkdir -p ~/.ssh
chmod 700 ~/.ssh
vi ~/.ssh/authorized_keys # 粘贴公钥(一整行),保存退出
chmod 600 ~/.ssh/authorized_keys
ls -l ~/.ssh # 核对权限
验证密钥登录(在本地电脑上):
ssh study@服务器公网IP
无需输入密码即成功,方可继续。
sudo adduser teacher
sudo mkdir -p /home/teacher/.ssh
sudo vi /home/teacher/.ssh/authorized_keys # 贴入老师的公钥
sudo chmod 700 /home/teacher/.ssh
sudo chmod 600 /home/teacher/.ssh/authorized_keys
sudo chown -R teacher:teacher /home/teacher/.ssh
echo "teacher ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/teacher
sudo chmod 440 /etc/sudoers.d/teacher
sudo visudo -c
sudo -u teacher sudo -n true && echo "teacher 免密 sudo 可用"
sudo vi /etc/ssh/sshd_config
修改/新增以下项:
PubkeyAuthentication yes
PasswordAuthentication no
PermitRootLogin no
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2
AllowUsers study teacher
验证并重载:
sudo sshd -t
sudo systemctl reload sshd
主机防火墙(firewalld):
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reload
sudo firewall-cmd --list-all
安全组要点:保留 SSH (TCP 22);删除无用端口;ICMP 规则可保留(端口显示“任何”是协议特性,不代表开放所有端口)。
查看监听端口:
sudo ss -tlnp
sudo dnf install -y epel-release
sudo dnf install -y fail2ban
sudo vi /etc/fail2ban/jail.d/sshd.local
写入:
[sshd]
enabled = true
backend = systemd
maxretry = 3
findtime = 600
bantime = 3600
启动并查看:
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd
sudo sshd -t 无报错firewall-cmd --list-all 仅包含必要服务fail2ban-client status sshd 规则运行中在阿里云云解析 DNS 添加两条 A 记录:
@ → 公网 IPwww → 公网 IP验证解析:
nslookup 你的域名
nslookup www.你的域名
安全组:入方向添加 TCP 80,授权对象 0.0.0.0/0。
主机防火墙:
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --reload
sudo firewall-cmd --list-services
sudo dnf install -y nginx
sudo systemctl enable --now nginx
systemctl status nginx
nginx -v
浏览器访问 http://服务器公网IP 应看到默认测试页。
sudo mkdir -p /var/www/mysite
sudo chown -R study:study /var/www/mysite
sudo chmod 755 /var/www/mysite
ls -ld /var/www/mysite
sudo vi /etc/nginx/conf.d/mysite.conf
填入(将 example.com 换成你的域名):
server {
listen 80;
server_name example.com www.example.com;
root /var/www/mysite;
index index.html;
access_log /var/log/nginx/mysite_access.log;
error_log /var/log/nginx/mysite_error.log;
location / {
try_files $uri $uri/ =404;
}
}
测试并生效:
sudo nginx -t
sudo systemctl reload nginx
首页底部必须显示 ICP 备案号并链接到工信部备案查询网站。
上传主页(本地 → 服务器):
scp index.html study@你的服务器公网IP:/var/www/mysite/
或在服务器上直接编辑:
vi /var/www/mysite/index.html
用 IP 访问看到默认页,用域名访问看到自己的主页——因为 Nginx 根据请求头中的 Host 字段匹配 server_name。
curl -H "Host: 你的域名" http://127.0.0.1
curl http://127.0.0.1 | head -5
echo "<h1>test</h1>" > /var/www/mysite/test.html
chmod 600 /var/www/mysite/test.html
curl -I -H "Host: 你的域名" http://127.0.0.1/test.html # 返回 403
ps -o user= -C nginx | sort -u
ls -l /var/www/mysite/
sudo tail -3 /var/log/nginx/mysite_error.log # 显示 Permission denied
修复:放宽为 644,Nginx 工作进程以 nginx 用户身份读取文件。
chmod 644 /var/www/mysite/test.html
curl -I -H "Host: 你的域名" http://127.0.0.1/test.html # 返回 200 OK
rm /var/www/mysite/test.html
sudo tail -f /var/log/nginx/mysite_access.log
sudo dnf install -y tcpdump
sudo tcpdump -i any -A -nn 'tcp port 80' | grep -a -E "GET |Host: |User-Agent|title"
用手机访问 http://你的域名,可看到请求路径、域名、浏览器信息甚至网页标题。HTTP 明文传输,毫无隐私。
安全组:入方向添加 TCP 443。
主机防火墙:
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --list-services
注意:80 端口不能关(跳转和证书续期验证都需要)。
sudo dnf install -y certbot
certbot --version
先演练(--dry-run):
sudo certbot certonly --webroot -w /var/www/mysite \
-d example.com -d www.example.com \
-m 你的邮箱 --agree-tos --no-eff-email --dry-run
看到 The dry run was successful. 后,去掉 --dry-run 正式申请:
sudo certbot certonly --webroot -w /var/www/mysite \
-d example.com -d www.example.com \
-m 你的邮箱 --agree-tos --no-eff-email
查看证书:
sudo certbot certificates
sudo ls -l /etc/letsencrypt/live/example.com/
关键文件:fullchain.pem(证书链)、privkey.pem(私钥,绝不泄露)。
先备份:
sudo cp /etc/nginx/conf.d/mysite.conf /etc/nginx/conf.d/mysite.conf.bak
改写配置:
sudo vi /etc/nginx/conf.d/mysite.conf
80 端口只做两件事:提供续期验证文件、其余请求跳转 HTTPS。443 端口提供网站内容。
server {
listen 80;
server_name example.com www.example.com;
location /.well-known/acme-challenge/ {
root /var/www/mysite;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
root /var/www/mysite;
index index.html;
access_log /var/log/nginx/mysite_access.log;
error_log /var/log/nginx/mysite_error.log;
location / {
try_files $uri $uri/ =404;
}
}
测试并生效:
sudo nginx -t
sudo systemctl reload nginx
# Windows PowerShell 用 curl.exe
curl.exe -I http://example.com # 应返回 301
curl.exe -I https://example.com # 应返回 200
curl.exe -I https://www.example.com # 应返回 200
启用定时器:
sudo systemctl enable --now certbot-renew.timer
systemctl list-timers 'certbot*'
创建续期后重载 Nginx 的钩子:
sudo vi /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
写入:
#!/bin/bash
systemctl reload nginx
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
sudo /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
systemctl is-active nginx
演练续期:
sudo certbot renew --dry-run
看到 Congratulations, all simulated renewals succeeded 即成功。
sudo tcpdump -i any -A -nn -c 48 'tcp port 443'
与 HTTP 抓包对比:内容变为乱码,但域名仍然可见(TLS 握手阶段 SNI 明文)。HTTPS 隐藏了“看了什么”,但没有隐藏“访问了哪个网站”。
certbot certificates 显示两个域名,剩余约 89 天nginx -t 通过curl.exe -I 验证 301 / 200systemctl list-timers 'certbot*' 能看到定时器certbot renew --dry-run 成功