第 1 讲 服务器安全加固 把一台"裸奔"的服务器加固成只认密钥、守得住、会反击的服务器
这一章做了什么
- 查看 SSH 登录日志,亲眼确认服务器正在被暴力破解扫描;
- 建立"两条通道"的认知:SSH(含 Workbench)走安全组和 sshd,VNC 是不走这些的救命后路;
- 创建普通用户
study并配置 SSH 密钥登录,安全地禁用密码登录和 root 远程登录; - 为老师创建巡检账号
teacher,安装的是老师的公钥(不是把我的私钥发出去); - 配置"安全组 + firewalld"两层防护,理解最小化原则;
- 安装并配置 fail2ban,让服务器自动封禁暴力破解者。
关键命令
# 查看谁在爆破我的服务器:筛出 sshd 日志中含 failed 的最后 30 行
sudo journalctl -u sshd | grep -i "failed" | tail -30
# 新建日常用户并加入 wheel 组(能用 sudo)
sudo adduser study
sudo passwd study
sudo usermod -aG wheel study
# 在自己电脑上生成密钥对(私钥永不外传)
ssh-keygen -t ed25519 -C "myname-服务器加固"
# 服务器上安装公钥:目录 700、文件 600,缺一不可
sudo su - study
mkdir -p ~/.ssh
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
# 为 teacher 配置免密 sudo(teacher 没有密码,wheel 的 sudo 要输本人密码,用不了)
echo "teacher ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/teacher
sudo chmod 440 /etc/sudoers.d/teacher
sudo visudo -c # 检查语法,全部 parsed OK 才安全
# 加固 sshd(先验证密钥能登录,再改这些收口)
sudo vi /etc/ssh/sshd_config
# PubkeyAuthentication yes / PasswordAuthentication no
# PermitRootLogin no / MaxAuthTries 3
# AllowUsers study teacher
sudo sshd -t # 先测语法,无输出即正确
sudo systemctl reload sshd # reload 不断开当前连接
# 防火墙:Rocky 上必须 --permanent + --reload,否则重启后失效
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reload
sudo firewall-cmd --list-all
sudo ss -tlnp # 对照实际监听的端口
# fail2ban:Rocky 默认源没有,先装 EPEL
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 # 看 Banned IP list
遇到的问题和解决办法
- 密钥登录总要求输密码:99% 是权限问题。
~/.ssh必须 700、authorized_keys必须 600 且属主是本人,SSH 权限过松会静默拒绝。 - 差点把自己锁在门外:靠"先验证密钥能登录,再禁用密码"的顺序避免;万一改崩,用控制台 VNC(不走 sshd、不走安全组)登录改回
sshd_config,sshd -t验证后 reload。 - firewalld 改了不生效:忘了加
--permanent或忘了--reload。 - fail2ban 装不上 / 规则不生效:需要 EPEL 源;Rocky 9 必须写
backend = systemd,网上读/var/log/secure的老教程在这里无效。 - 搞懂了一个疑惑:安全组里 ICMP 规则端口显示"任何 / -1"不是漏洞——ICMP 是网络层协议,本身没有端口概念,放行它只意味着能被 ping 通。
本章收获的三个习惯:私钥永不外传、改配置先留后路(VNC)、先验证再收口。
第 2 讲 网站上线 域名解析 + Nginx 个人主页 + 备案号
这一章做了什么
- 给已备案域名添加 A 记录(
@和www各一条),并验证解析生效; - 在安全组和 firewalld 放行 80 端口,把网站这扇门打开;
- 安装 Nginx,编写站点配置,发布带备案号的个人主页;
- 亲手制造并修复一次 403 故障,学会看访问日志和错误日志。
关键命令
# 在自己电脑上验证域名解析(记录类型 A,主机记录 @ 和 www,TTL 默认 10 分钟)
nslookup 我的域名
nslookup www.我的域名
# 防火墙放行 HTTP
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --reload
sudo firewall-cmd --list-services # 应包含 ssh 和 http
# 安装并启动 Nginx
sudo dnf install -y nginx
sudo systemctl enable --now nginx
systemctl status nginx
# 准备网站目录(交给 study,以后不用 sudo 就能改网页)
sudo mkdir -p /var/www/mysite
sudo chown -R study:study /var/www/mysite
sudo chmod 755 /var/www/mysite
# 站点配置 /etc/nginx/conf.d/mysite.conf
# listen 80; server_name example.com www.example.com;
# root /var/www/mysite; index index.html;
sudo nginx -t # 改完配置先测语法
sudo systemctl reload nginx
# 上传主页(scp 用的是同一把密钥,不需要密码)
scp index.html study@服务器公网IP:/var/www/mysite/
# 验证 server_name 机制:Host 不同,返回的站点不同
curl -H "Host: 我的域名" http://127.0.0.1 # 返回我的主页
curl http://127.0.0.1 | head -5 # 返回 Nginx 默认页
# 403 排障实验
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 # 工作进程以 nginx 用户运行
sudo tail -3 /var/log/nginx/mysite_error.log # 看到 Permission denied
chmod 644 /var/www/mysite/test.html # 修复:其他人可读 → 200
# 看见第一位访客
sudo tail -f /var/log/nginx/mysite_access.log
遇到的问题和解决办法
- 403 Forbidden:文件是 study 的,但 Nginx 以
nginx用户读取,600 权限"其他人"读不了。思路:先看错误日志找原因,再用ls -l核对权限——网页文件至少 644,目录至少 755。 - 用 IP 访问看到的是默认页:这不是故障。Nginx 靠请求头里的 Host 匹配
server_name,用 IP 访问匹配不上,就交给默认站点。 - 域名打不开但 IP 能打开:解析还没生效,用
nslookup检查,等 TTL 过后再试——所以解析要最先做,让等待发生在后台。 - 连接超时:80 端口没开,安全组和 firewalld 两层都要检查。
- 404:文件名不是
index.html或 root 路径写错。
合规硬性要求:首页底部必须显示 ICP 备案号并链接到 https://beian.miit.gov.cn/ ;网站开通后 30 日内还需办理公安联网备案。
第 3 讲 HTTPS 与证书自动续期 让网站从不安全的 http:// 变成带安全锁的 https://
这一章做了什么
- 先用 tcpdump 抓 80 端口的包,亲眼看到 HTTP 明文里 GET 请求、User-Agent、网页标题一览无余;
- 在安全组和 firewalld 放行 443 端口(80 不能关:跳转和证书续期都要用);
- 用 certbot 以 webroot 方式申请 Let's Encrypt 免费证书(先
--dry-run演练再正式申请); - 改写 Nginx 配置:80 只做续期验证和 301 跳转,443 真正提供内容;
- 启用自动续期定时器 + deploy 钩子;最后再抓一次 443 的包,对比加密前后的区别。
关键命令
# 抓包看看 HTTP 有多"裸":-i any 全网卡,-A 文本显示,-nn 不解析名称
sudo dnf install -y tcpdump
sudo tcpdump -i any -A -nn 'tcp port 80' | grep -a -E "GET |Host:|User-Agent|"
# 放行 443
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
# 安装 certbot,先演练再正式申请(防止一小时内失败次数超限被暂时拒绝)
sudo dnf install -y certbot
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 certificates # 查看证书域名、到期时间(约 90 天)
sudo openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem \
-noout -issuer -dates -ext subjectAltName # 核对颁发者、有效期、域名列表
# HTTPS 站点配置要点(mysite.conf 拆成两个 server 块)
# 80: location /.well-known/acme-challenge/ { root /var/www/mysite; }
# location / { return 301 https://$host$request_uri; }
# 443: listen 443 ssl;
# ssl_certificate .../fullchain.pem;
# ssl_certificate_key .../privkey.pem;
# ssl_protocols TLSv1.2 TLSv1.3;
sudo nginx -t
sudo systemctl reload nginx
# 自动续期:定时器 + deploy 钩子
sudo systemctl enable --now certbot-renew.timer
systemctl list-timers 'certbot*'
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 certbot renew --dry-run # 模拟续期,验证流程是通的
# 对比实验:加密后的流量
sudo tcpdump -i any -A -nn -c 40 'tcp port 443'
遇到的问题和解决办法
- certbot 报 DNS problem: NXDOMAIN:域名或 www 子域名缺解析记录,
nslookup检查后补 A 记录。 - certbot 报 unauthorized:验证文件访问不到——核对
-w目录是否与 Nginx 的 root 一致、server_name是否包含该域名。 - nginx -t 报 cannot load certificate:证书路径里域名写错,对照
certbot certificates的输出核对目录名。 - 锁的图标带警告:页面里还引用了
http://开头的资源,改成https://或相对路径。 - 改坏了想恢复:改配置前先备份,且备份文件名不能以 .conf 结尾(否则会被 Nginx 一并加载造成重复配置):
cp mysite.conf mysite.conf.bak。
两个重要认知:① 证书私钥 privkey.pem 和第 1 讲的 SSH 私钥同理,谁拿到谁就能冒充我的网站,绝不外泄;② HTTPS 加密的是"看了什么内容",但"访问了哪个网站"(域名)在建立连接时仍是明文。80 端口不能关:HTTP→HTTPS 跳转和证书续期验证都靠它。