服务器安全加固:让第一台服务器学会自我防护
关键词:SSH 密钥 · 禁用密码 · 安全组与 firewalld · fail2ban · VNC 自救
服务器接入公网那一刻就不再是无人的角落。加固的主线只有一条——沿着“攻击者会从哪里进来”逐处把门堵死。
1.1先看见威胁
公网上有大量自动化扫描程序昼夜遍历 IP,一旦发现 22 端口开着,就用海量用户名密码组合反复尝试——这就是暴力破解。先把证据调出来:
# journalctl -u sshd:查看 sshd 服务日志 # grep -i "failed":只筛登录失败行(-i 忽略大小写) # tail -30:只看最后 30 条 sudo journalctl -u sshd | grep -i "failed" | tail -30
输出里通常会有大量陌生 IP 的失败记录,而且绝大多数在试 root。
1.2两条登录通道:只有一条能救命
| 通道 | 走什么路 | 受什么影响 | 能不能救命 |
|---|---|---|---|
| SSH / Workbench | 公网 → 安全组 22 端口 → sshd | sshd 配置改坏、22 端口封掉,一起断 | 不能,它不是后路 |
| VNC | 阿里云带外管理通道,相当于坐在物理显示器前 | 不经过安全组,也不经过 sshd | 能,唯一的后路 |
登录后用 tty 可以直接印证这一点:VNC 显示 /dev/tty1(本地控制台终端),SSH 显示 /dev/pts/0(伪终端,一条网络连接)。
后路只有亲自走通过一次才靠得住。动手改任何配置之前,先用 VNC 完整登录一次。VNC 粘贴支持差,适合救急不适合日常;忘了系统密码可在控制台“重置实例密码”,重启生效。
1.3用密钥代替密码
密码有两个天生弱点:可能被猜中、可能被泄露。密钥认证换了一套思路——一对相互配对的钥匙:
- 私钥:只留在自己电脑上,绝不外传,是身份的唯一凭证;
- 公钥:放到服务器上,可以公开。
登录时服务器用你的公钥出一道只有对应私钥才能解开的数学难题:有私钥解得开,没私钥拿到公钥也无从下手。
准备普通用户
sudo adduser study # 新建普通用户 sudo passwd study # 设置登录密码(输入两次) sudo usermod -aG wheel study # 加入 wheel 组,才能用 sudo
在自己电脑上生成密钥对
# -t ed25519:现代、短小而安全的算法 # -C "...":备注,方便日后辨认这是哪把钥匙 ssh-keygen -t ed25519 -C "你的名字-服务器加固"
一路回车即可(可不设密码短语)。查看公钥:
# Windows type $env:USERPROFILE\.ssh\id_ed25519.pub # Mac / Linux cat ~/.ssh/id_ed25519.pub
复制整行(以 ssh-ed25519 AAAA... 开头),必须是一整行,中间不能换行。
把公钥装到服务器
“安装公钥”就是把公钥写进该账号的 ~/.ssh/authorized_keys。
sudo su - study # 切换用户,- 表示加载其环境 mkdir -p ~/.ssh # -p:目录已存在也不报错 chmod 700 ~/.ssh # 仅本人可进入 vi ~/.ssh/authorized_keys # 按 i 粘贴 → Esc → :wq 保存 chmod 600 ~/.ssh/authorized_keys # 仅本人可读写,SSH 强制要求 ls -l ~/.ssh # 应显示 -rw-------,属主是自己
权限过松,SSH 会直接拒绝使用 authorized_keys,而且不报任何错。很多初学者就在这卡半天。.ssh 必须 700,authorized_keys 必须 600,属主必须是本人(不是 root)。
铁律:先验证密钥能登录,再往下做
ssh study@服务器公网IP
能直接登入且没要密码,说明配置成功。失败就回控制台查:公钥有没有贴错、权限对不对、用户名对不对。验证成功之前,绝不进行下一步。
1.4给老师配一个巡检账号
几乎人人都会犯的直觉错误:“把我的私钥发给老师”。绝对不行——私钥一旦发出,任何拿到它的人都能完全控制这台机器。正确做法恰恰相反:让老师拥有自己的密钥对,你只把老师的公钥装到服务器上。
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 # teacher 没有密码,而 wheel 组 sudo 必须输本人密码,所以单独配免密 echo "teacher ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/teacher sudo chmod 440 /etc/sudoers.d/teacher # sudoers 规则要求 440 sudo visudo -c # 语法检查,全部 parsed OK 才行 sudo -u teacher sudo -n true && echo "teacher 免密 sudo 可用"
sudo 配置写错,所有账号的 sudo 都会失效,包括你自己的 study。所以每次改完必跑 visudo -c;真出事就用 VNC 以 root 登录修复。
1.5收口:加固 sshd 配置
sudo vi /etc/ssh/sshd_config PubkeyAuthentication yes # 允许密钥登录(加固的基础) PasswordAuthentication no # 禁止密码登录 ← 确认密钥可用后再改 PermitRootLogin no # 禁止 root 直接远程登录 MaxAuthTries 3 # 单连接最多 3 次认证,拖慢爆破 ClientAliveInterval 300 # 每 300 秒探测客户端是否在线 ClientAliveCountMax 2 # 连续 2 次无响应即断开,清理空闲会话 AllowUsers study teacher # 登录白名单,其余一律拒绝
两项格外小心:PasswordAuthentication no 是“先验证再关”的对象;AllowUsers 是白名单,写错用户名或漏掉 teacher,对应的人就被挡在门外。
sudo sshd -t # 无输出即正确;有错会指出行号 sudo systemctl reload sshd # reload 不会断开当前连接(比 restart 安全)
1.6两道门:安全组与主机防火墙
| 层次 | 谁管的 | 管到哪一步 |
|---|---|---|
| 安全组 | 阿里云平台层防火墙 | 公网流量能不能进入你的实例 |
| firewalld | 操作系统内部的防火墙 | 流量到达实例后,放不放行到具体服务 |
两层相互独立、都要配,原则是同一个词——最小化:只开必须的门。
sudo firewall-cmd --permanent --add-service=ssh # 写永久规则 sudo firewall-cmd --reload # 重载才真正生效 sudo firewall-cmd --list-all # 核对放了哪些服务/端口
改防火墙规则必须加 --permanent 再 --reload,否则重启后规则失效。
读懂“端口为什么写任何”
安全组按“协议 + 端口”两个维度控制,不是只看端口:
- 只有 TCP / UDP 才有端口概念,所以 SSH 写作
TCP 22、HTTP 写作TCP 80;放行 TCP 22 就是开了一扇能进来做事的门。 - ICMP 是网络层诊断协议(ping / traceroute 用它),本身不使用端口,控制台只能填
-1/-1或显示“任何”,那只是占位符,不代表把所有端口都打开了。
放行 ICMP 只是让别人能 ping 通你(知道在线),既无法登录也无法传数据。保留它还有好处:SSH 连不上时先 ping 一下,就能判断问题在网络/机器,还是只在 SSH 配置。
# -t 只看 TCP -l 只看监听 -n 用数字不解析域名 -p 显示进程
sudo ss -tlnp
1.7让服务器自动反击:fail2ban
前面的加固都是被动防守(把门锁牢);fail2ban 让它主动反击:盯着登录日志,发现某 IP 反复失败就自动封禁一段时间——从“守得住”升级为“会还手”。
sudo dnf install -y epel-release # 先装 EPEL 扩展源 sudo dnf install -y fail2ban # 再装本体
规范做法是在 jail.d/ 下新建 .local 文件,而不是改主配置 jail.conf——软件升级时自己的配置不会被覆盖。
sudo vi /etc/fail2ban/jail.d/sshd.local [sshd] enabled = true # 启用本规则 backend = systemd # Rocky 9 日志由 systemd 管理,别照抄读 /var/log/secure 的老写法 maxretry = 3 # 最大失败次数 findtime = 600 # 统计窗口 600 秒(10 分钟) bantime = 3600 # 封禁 3600 秒(1 小时)
连起来读就是一句话:10 分钟内失败 3 次,把这个 IP 封一小时。
sudo systemctl enable --now fail2ban # 启动 + 开机自启 sudo fail2ban-client status sshd # 重点看 Banned IP list
封禁名单为空不代表操作有误,只说明这台机器还没被扫描器盯上。规则已生效,挂一段时间自然会有人“进小黑屋”。
1.8验证清单与自救
- VNC 能以 study 登录(
tty显示/dev/tty1) - 本机
ssh study@公网IP免密登录成功 - 密码登录被拒绝、root 无法直接登录
sudo sshd -t无报错firewall-cmd --list-all只有 ssh,没有多余端口fail2ban-client status sshd规则运行中- teacher 已装老师的公钥,
sudo -u teacher sudo -n true成功
| 症状 | 真实原因 / 处理 |
|---|---|
| SSH 连不上 | 用 VNC 登录,把 sshd_config 改回来,再 sshd -t + reload |
| 密钥登录仍要密码 | 99% 是权限:.ssh 700、authorized_keys 600、属主是本人 |
| 老师连不上 | AllowUsers 漏了 teacher,或 .ssh 属主没改回 teacher |
| 所有账号 sudo 全废 | VNC 以 root 修正/删除 /etc/sudoers.d/teacher,再 visudo -c |
| firewalld 不生效 | 忘了 --permanent 或忘了 --reload |
本讲小结
- 猜密码登进来 → 密钥登录 + 禁密码 + 禁 root
- 从别的端口钻进来 → 安全组 + firewalld,两层最小化
- 反复爆破 → fail2ban 自动封禁
- 进来了不被发现 → 日志审计
- 三个受用终身的习惯:私钥永不外传、改配置先留后路、先验证再收口
网站上线:域名解析与 Nginx 个人主页
关键词:DNS A 记录 · 80 端口 · Nginx 站点配置 · server_name · 403 排障
让别人用域名看到你的主页,需要同时打通三个环节:DNS 指路、端口开门、Nginx 发文件。缺一个,网站就是打不开。
上一章已禁用 SSH 密码登录,此后统一用自己电脑的 SSH 客户端登录(Workbench 需上传私钥,不推荐;VNC 留作救急)。另外 teacher 没有密码,上一章若漏了免密规则,先用 study 账号补上 /etc/sudoers.d/teacher(内容与第 1 讲 1.4 相同),并跑 visudo -c 验证。
2.1三个环节
| 环节 | 做什么 | 出问题时的表现 |
|---|---|---|
| DNS | 添加 A 记录,域名指向服务器 IP | 域名打不开,IP 能打开 |
| 开门 | 安全组 + firewalld 放行 80 | 连接超时 |
| Nginx | 安装、写站点配置、发布主页 | 403 / 404 / 看到默认页 |
DNS 是互联网的“通讯录”:浏览器输入域名后,先向 DNS 查对应 IP,再去连那台服务器。
2.2域名解析
为什么先做解析:记录添加后要一段时间才全网生效。第一步就做它,让它在后台慢慢生效,同时去做后面的步骤——把需要等待的事提前做,是运维常用思路。
三个字段:
- 记录类型:本章用
A,意思是“把域名指向一个 IPv4 地址”; - 主机记录:
@代表域名本身,www代表www.域名; - TTL:各地 DNS 的缓存时长,默认 10 分钟,即改完最多约 10 分钟各地才更新。
控制台:云解析 DNS → 域名解析 → 解析设置 → 添加记录。加两条 A 记录,主机记录分别填 @ 和 www,记录值都是服务器公网 IP,TTL 默认。
# 看 Address 一栏是不是你的服务器公网 IP
nslookup 你的域名
nslookup www.你的域名
查不到或 IP 不对先别急,可能还在生效,继续往后做,几分钟后再查。
2.3为网站开门(80)
“最小化”不是“什么都不开”,而是只在服务真正上线时才开对应的门。本章只开 80;443 等下一章配证书再开。两层门都要开:
- 安全组:入方向添加规则,协议 TCP,端口 80,授权对象
0.0.0.0/0(控制台通常有“HTTP(80)”快速添加)。
sudo firewall-cmd --permanent --add-service=http # 永久放行 TCP 80 sudo firewall-cmd --reload # 使规则生效 sudo firewall-cmd --list-services # 应包含 ssh 和 http
2.4安装 Nginx 并配置站点
sudo dnf install -y nginx sudo systemctl enable --now nginx # 启动 + 开机自启 systemctl status nginx # 应为 active (running),按 q 退出 nginx -v # 版本号
浏览器访问 http://服务器公网IP,看到 Nginx 默认测试页即安装成功、80 也通了。
准备网站目录
不改系统默认页面,为自己的站点单独建目录,结构清晰,日后加第二站点也互不干扰。
sudo mkdir -p /var/www/mysite sudo chown -R study:study /var/www/mysite # 之后改网页不必 sudo sudo chmod 755 /var/www/mysite # 属主可读写,其他人(含 Nginx)可进入 ls -ld /var/www/mysite # 应显示 drwxr-xr-x getenforce # 阿里云 Rocky 镜像默认 Disabled,本章不涉及 SELinux
网上很多“Rocky 下 403”的讨论是 SELinux 导致。阿里云镜像默认关闭,但将来在自建 Rocky 上遇到 403,记得把 SELinux 也列进怀疑名单。
写站点配置
sudo vi /etc/nginx/conf.d/mysite.conf
server {
listen 80; # 监听 80,接收 HTTP 请求
server_name example.com www.example.com; # 带 www 和不带 www 都要写
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; # 找不到就 404
}
}
server_name 是最关键的一行:一台服务器能跑许多网站,Nginx 靠请求头里的 Host 字段区分——域名匹配哪个站点的 server_name,就交给谁处理。下一章申请证书时,certbot 也靠这一行识别你的域名。
sudo nginx -t # 显示 syntax is ok 和 test is successful 才算过 sudo systemctl reload nginx # reload 不中断正在进行的访问
2.5发布个人主页
网站首页底部必须显示 ICP 备案号,并链接到工信部备案查询网站 https://beian.miit.gov.cn/。这是备案网站的合规要求,不是可选项。另外按规定网站开通后 30 日内还需办理公安联网备案,取得备案号后同样要显示在底部(以当地规定为准)。
AI 生成的主页必须自己核对:备案号是不是自己的、链接对不对、页面能否正常显示。能讲清页面结构才算真正完成。
# 方式一:本地写好,用 scp 上传(与 ssh 同一把密钥,免密) scp index.html study@你的服务器公网IP:/var/www/mysite/ # 方式二:直接在服务器上编辑(目录已归 study,无需 sudo) vi /var/www/mysite/index.html
为什么用 IP 访问看到的是默认页
因为 server_name 在起作用:用 IP 访问时 Host 不是你的域名,匹配不上你的站点,就交给系统默认站点处理。可以这样亲眼验证:
curl -H "Host: 你的域名" http://127.0.0.1 # 返回你的主页 curl http://127.0.0.1 | head -5 # 返回默认测试页
2.6排障实验:亲手制造一个 403
403 最常见的原因就是文件权限不对。亲手做一次,再用日志找出病根:
echo "<h1>test</h1>" > /var/www/mysite/test.html chmod 600 /var/www/mysite/test.html # 只有 study 能读 curl -I -H "Host: 你的域名" http://127.0.0.1/test.html # 结果 403 Forbidden
文件明明属于 study,为什么 Nginx 读不了?因为 Nginx 不是以 study 身份运行的:
ps -o user= -C nginx | sort -u # root(主进程)+ nginx(工作进程) ls -l /var/www/mysite/ # test.html 是 -rw------- sudo tail -3 /var/log/nginx/mysite_error.log # 有 (13: Permission denied)
真正读取网页文件的是工作进程,身份是 nginx 用户。对它来说 test.html 属于“其他人”,而 600 不允许其他人读,于是 403。错误日志直接说明了原因。
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 # 实验结束
先看错误日志找原因,再用 ls -l 核对权限。网页文件至少 644,目录至少 755,Nginx 才能正常读取。
2.7看见第一位访客
sudo tail -f /var/log/nginx/mysite_access.log
对方每刷新一次,屏幕就多一行:来访 IP、时间、访问页面、状态码、浏览器。日志是运维最重要的信息来源——网站出问题,第一件事就是看日志。
2.8验证清单与故障表
nslookup 域名(含 www)返回服务器公网 IP- 安全组放行 TCP 80,
firewall-cmd --list-services含 http systemctl is-enabled nginx显示 enabled,nginx -t通过- 浏览器访问域名与 www 域名都显示自己的主页
- 底部备案号可点击跳转工信部
- 访问日志里能看到记录
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 域名打不开,IP 能打开 | 解析未生效 / 记录填错 | nslookup 查结果,等 TTL 后再试 |
| 连接超时 | 80 端口没开 | 查安全组入方向 + firewall-cmd --list-services |
| 403 Forbidden | 文件或目录权限不足 | 先看错误日志,再 ls -l;文件 644、目录 755 |
| 404 Not Found | 文件名或路径不对 | 确认是 index.html,确认 root 路径正确 |
| 域名访问仍是默认页 | server_name 写错或漏 www | 改配置 → nginx -t → reload |
| 启动/重载失败 | 配置语法错误 | nginx -t 看报错行号;journalctl -u nginx |
本讲小结
- DNS 加 A 记录 → 域名指向服务器 IP
- 安全组 + firewalld 放行 80 → 门打开了
- Nginx 装好、写好 server 块、发主页 → 内容出去了
- 今后排障就按 DNS → 端口 → Nginx 三个环节从前往后查
给网站加上安全锁:HTTPS 与证书自动续期
关键词:明文抓包 · CA 与 HTTP-01 · certbot · 301 跳转 · systemd 定时器 + deploy 钩子
HTTP 是明信片,路上谁都能看;HTTPS 是上了锁的信封。但锁只有 90 天有效期,所以自动续期不是加分项,是必做项。
http://域名 与 http://www.域名 都能看到主页。本章申请证书要用到这两个域名,缺一不可。
3.1先亲眼看看“裸奔”
HTTP 传输的一切都是明文,中间经过的每台设备都能原样看到你访问了什么、提交了什么。装个抓包工具亲自验证:
sudo dnf install -y tcpdump # -i any:所有网卡抓包 -A:以文本显示内容 -nn:IP/端口用数字 # 'tcp port 80':只抓 HTTP grep -a -E:只留请求行/域名/UA/标题 sudo tcpdump -i any -A -nn 'tcp port 80' | grep -a -E "GET |Host:|User-Agent|<title>"
让同学用手机访问你的 http 站点,屏幕上会直接出现:请求了哪个页面(GET /)、访问的是哪个域名(Host:)、用的什么手机浏览器(User-Agent:),甚至网页标题。如果页面有登录表单,账号密码也会被看得一清二楚。按 Ctrl+C 停止。
3.2HTTPS 与证书的原理
HTTPS = HTTP + TLS 加密,解决两个问题:
- 加密:传输内容变成只有浏览器和服务器能解开的密文;
- 身份认证:证明你连的确实是这个域名的真实服务器,而不是冒充者。
身份认证靠数字证书——网站的“身份证”,上面写明属于哪些域名、谁颁发、何时到期。证书必须由 CA(证书颁发机构)签发,浏览器和系统内置了受信任 CA 名单,只有名单上的 CA 签发的证书才会显示安全锁。本章用 Let's Encrypt:免费、全自动的公益 CA。
CA 怎么确认域名是你的
本章用 HTTP-01 验证:
- 服务器上运行 certbot,向 Let's Encrypt 申请证书;
- Let's Encrypt 给一串随机内容,要求放到
http://域名/.well-known/acme-challenge/随机文件名; - certbot 把文件写进
/var/www/mysite; - Let's Encrypt 从公网访问该地址,读到正确内容 → 证明你控制着这个域名和服务器 → 签发。
域名必须正确解析到你的服务器;80 端口必须对外开放(验证走 HTTP);Nginx 必须能把验证文件发出去(server_name 与 root 都要对)。这三条也正是申请失败时的三个排查方向。
Let's Encrypt 证书有效期只有 90 天,并宣布将在 2028 年 2 月前逐步缩短到 45 天;同时已于 2025 年停止发送到期提醒邮件。靠人记着续期,迟早会忘。
3.3开放 443
照“服务上线才开门”的原则,现在开放 443,两层门都要开:
- 安全组:入方向,TCP 443,授权对象
0.0.0.0/0(有“HTTPS(443)”快速添加)。
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --list-services # 应包含 ssh、http、https
一是有人直接输 http:// 时需要 80 把他引导到 HTTPS;二是每次续期 Let's Encrypt 仍要通过 80 验证。关掉 80,三个月后证书就无法续期。
3.4申请证书
sudo dnf install -y certbot certbot --version
Let's Encrypt 对失败有次数限制:同一域名一小时内失败太多次会被暂时拒绝。--dry-run 会走完整流程但连接测试环境,既不发证书也不占正式次数。和“改配置先用 -t 测试”是同一个习惯。
# certonly:只申请证书,不自动改 Nginx(配置自己写才看得懂) # --webroot:把验证文件放进网站目录,由 Nginx 发出去 # -w:网站目录,即 Nginx 里的 root # -d:证书包含的域名,带 www 和不带 www 各写一个 # -m:联系邮箱 --agree-tos:同意条款 --no-eff-email:不订阅 EFF 邮件 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. 即演练成功;失败就按“解析、80 端口、Nginx”三个方向逐一排查,修好后重跑。
sudo certbot certonly --webroot -w /var/www/mysite \
-d example.com -d www.example.com \
-m 你的邮箱 --agree-tos --no-eff-email
看到 Successfully received certificate. 即签发成功。
看看你的证书
sudo certbot certificates # 域名、到期时间、剩余天数、文件位置 sudo ls -l /etc/letsencrypt/live/example.com/ # 目录名取自第一个 -d 域名
| 文件 | 内容 | 用途 |
|---|---|---|
fullchain.pem | 证书 + 中间证书 | 发给访问者,证明网站身份 |
privkey.pem | 证书私钥 | 留在服务器解密,绝不能泄露 |
cert.pem | 仅你的证书 | 本章不直接用 |
chain.pem | 仅中间证书 | 本章不直接用 |
privkey.pem 和第 1 讲的 SSH 私钥同理:谁拿到它,谁就能冒充你的网站。它所在目录只有 root 能访问,这是 certbot 有意设置的保护。
# -noout 不输出原始编码 -issuer 颁发者 -dates 生效/到期 -ext 域名列表
sudo openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem \
-noout -issuer -dates -ext subjectAltName
可以交给 AI 逐项解释,但自己一定要核对两件事:到期时间是否大约在 90 天之后?域名列表里两个域名是否都在?
3.5配置 Nginx 启用 HTTPS
sudo cp /etc/nginx/conf.d/mysite.conf /etc/nginx/conf.d/mysite.conf.bak
Nginx 会自动加载 conf.d/ 下所有 .conf 文件。备份名若也以 .conf 结尾,会被一并加载,造成重复配置。
现在网站分成两部分:80 只负责两件事——为续期提供验证文件、把其余请求跳转到 HTTPS;443 才真正提供内容。
# ① 80 端口:验证文件 + 跳转 server { listen 80; server_name example.com www.example.com; location /.well-known/acme-challenge/ { root /var/www/mysite; # 续期时仍靠 HTTP 读这里的文件 } location / { return 301 https://$host$request_uri; # 永久跳转到同一地址的 HTTPS } } # ② 443 端口:真正提供内容 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; } }
$host 与 $request_uri 分别是访问者请求的域名和路径,所以访问 http://www.example.com/about.html 会准确跳到 https://www.example.com/about.html,而不是一律跳回首页。
sudo nginx -t # 必须 syntax is ok + test is successful
sudo systemctl reload nginx
nginx -t 报 cannot load certificate,多半是证书路径里的域名写错,对照 3.4 的 ls 结果核对目录名。
验证
浏览器访问 http://域名 会自动变 https 并出现锁图标,点开可看颁发者与有效期,应与 3.4 查到的一致。
curl.exe -I http://example.com # 301,Location 指向 https://example.com/ curl.exe -I https://example.com # 200 OK curl.exe -I https://www.example.com # 200 OK
3.6配置自动续期
certbot 自带 certbot-renew.timer,每天检查,到期前约 30 天自动续期。安装后不一定已启用,所以主动启用并确认:
sudo systemctl enable --now certbot-renew.timer # 启用并开始计时 systemctl list-timers 'certbot*' # NEXT 一列是下次检查时间
续期后自动重载 Nginx
续期只更新磁盘上的证书文件,Nginx 不会自动读新证书,必须 reload。certbot 提供“钩子”机制:放在 /etc/letsencrypt/renewal-hooks/deploy/ 下的脚本会在每次续期成功后自动执行。
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 # 应显示 active
sudo certbot renew --dry-run # 看到 all simulated renewals succeeded 即通
演练时 certbot 会提示跳过 deploy 钩子——演练不产生新证书,自然不需要重载 Nginx。钩子脚本本身已在 3.6 手动验证过。
3.7再抓一次包:HTTPS 的流量长什么样
sudo tcpdump -i any -A -nn -c 40 'tcp port 443'
同学访问 https 站点时对比:这次看不到 GET、User-Agent、网页标题,满屏乱码——内容已加密。
但仔细看,可能从乱码里认出你的域名:浏览器建加密连接前要先告诉服务器“我要访问哪个网站”,服务器才能拿出对应证书,这一步是明文的。
HTTPS 隐藏的是你看了什么内容,而不是你访问了哪个网站。
3.8验证清单与故障表
- 安全组与 firewalld 都放行 443,且 80 仍开放
certbot certificates显示两个域名,剩余约 89 天nginx -t通过- http 自动跳 https,地址栏有锁
curl.exe -I:http 返回 301,两个 https 域名都返回 200systemctl list-timers 'certbot*'能看到定时器certbot renew --dry-run成功- 主页底部备案号仍正常显示
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
certbot 报 DNS problem: NXDOMAIN | 域名或 www 没有解析记录 | nslookup 两个域名,补上缺失的 A 记录 |
certbot 报 Timeout during connect | 80 端口不通 | 查安全组与 firewall-cmd --list-services |
certbot 报 unauthorized 且有 404 | 验证文件访问不到 | 核对 -w 是否等于 Nginx 的 root,server_name 是否含该域名 |
too many failed authorizations | 一小时内失败次数过多 | 等一小时,之后先 --dry-run 成功再正式申请 |
nginx -t 报 cannot load certificate | 证书路径写错 | 对照 certbot certificates 的路径修改 |
| https 访问超时 | 443 没开 | 检查安全组与 firewalld |
| http 没有跳转 | 配置未生效或浏览器缓存 | reload 后用 curl.exe -I 直接看响应 |
| 锁图标带警告 | 页面引用了 http:// 的图片/脚本 | 把资源地址改成 https 或相对路径 |
| 改坏了想恢复 | — | cp mysite.conf.bak mysite.conf,再 nginx -t 与 reload |
本讲小结
- 开门:放行 443,但 80 不能关(跳转与续期都要用)
- 申请:certbot 以 webroot 方式,先
--dry-run再正式 - 配置:80 负责跳转,443 提供内容;
privkey.pem绝不外泄 - 续期:systemd 定时器 + deploy 钩子,90 天靠自动化而不是靠人记
把三讲串起来:一条主线、三个习惯
复盘比记命令更重要
| 讲 | 解决了什么 | 核心命令 |
|---|---|---|
| 第 1 讲 | 服务器只认密钥、守得住、会还手 | ssh-keygen / sshd -t / firewall-cmd / fail2ban-client |
| 第 2 讲 | 域名能访问、主页能打开 | nslookup / nginx -t / tail -f |
| 第 3 讲 | 传输加密、身份可信、证书不过期 | certbot certonly / certbot renew --dry-run |
一条贯穿的技术主线
三讲其实在反复做同一件事:先看清现状(看日志 / 抓包 / ping),再最小化改动,改完先测语法,最后才让它生效。
sshd -t → firewall-cmd --reload → nginx -t → certbot --dry-run,是同一个动作在不同层的重复:先验证,再收口。
三个受用终身的习惯
- 私钥永不外传:SSH 私钥、
privkey.pem,谁拿到谁就是你。要给别人权限,装他的公钥,不发自己的私钥。 - 改配置先留后路:改 sshd 前先走通 VNC,改 Nginx 前先
cp一份.bak。 - 先验证再收口:密钥能登再禁密码,语法测过再 reload,dry-run 过了再正式申请。
自测:这五个问题能答上来吗
- 为什么“把私钥发给老师”是错的?公钥和私钥在认证里各扮演什么角色?
- Workbench 和 VNC,哪个会受
PasswordAuthentication no影响? - 安全组里 ICMP 的端口显示“任何”,是不是所有端口都开了?
- 网页文件属于 study,为什么改成 600 后 Nginx 就读不了?它是以谁的身份运行?
- 证书有效期从 90 天缩到 45 天、CA 也不再发提醒,对运维意味着什么?