服务器运维 · 模块一

我的运维笔记

从一台“裸奔”的服务器,到一台带 HTTPS 安全锁的网站。三讲讲清一件事:把门一扇一扇锁上,再把网站一步一步放出去。

环境 阿里云 ECS · Rocky Linux 9.8 · 2 核 2G 整理 听课笔记 · 3 讲 形式 单文件静态页
LECTURE 01

服务器安全加固:让第一台服务器学会自我防护

关键词: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用密钥代替密码

密码有两个天生弱点:可能被猜中、可能被泄露。密钥认证换了一套思路——一对相互配对的钥匙:

登录时服务器用你的公钥出一道只有对应私钥才能解开的数学难题:有私钥解得开,没私钥拿到公钥也无从下手。

准备普通用户

新建日常账号(把 study 换成自己的用户名)
sudo adduser study                # 新建普通用户
sudo passwd study                 # 设置登录密码(输入两次)
sudo usermod -aG wheel study      # 加入 wheel 组,才能用 sudo

在自己电脑上生成密钥对

Windows 用 PowerShell,Mac/Linux 用终端
# -t ed25519:现代、短小而安全的算法
# -C "...":备注,方便日后辨认这是哪把钥匙
ssh-keygen -t ed25519 -C "你的名字-服务器加固"

一路回车即可(可不设密码短语)。查看公钥:

取出公钥(.pub 结尾的是公钥,另一个是私钥)
# 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给老师配一个巡检账号

几乎人人都会犯的直觉错误:“把我的私钥发给老师”。绝对不行——私钥一旦发出,任何拿到它的人都能完全控制这台机器。正确做法恰恰相反:让老师拥有自己的密钥对,你只把老师的公钥装到服务器上。

创建 teacher 账号 + 免密 sudo
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 配置

编辑 /etc/ssh/sshd_config 并逐项修改
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操作系统内部的防火墙流量到达实例后,放不放行到具体服务

两层相互独立、都要配,原则是同一个词——最小化:只开必须的门。

firewalld:只放行 SSH
sudo firewall-cmd --permanent --add-service=ssh   # 写永久规则
sudo firewall-cmd --reload                        # 重载才真正生效
sudo firewall-cmd --list-all                      # 核对放了哪些服务/端口
Rocky 上最易忽略的细节

改防火墙规则必须加 --permanent 再 --reload,否则重启后规则失效。

读懂“端口为什么写任何”

安全组按“协议 + 端口”两个维度控制,不是只看端口:

放行 ICMP 只是让别人能 ping 通你(知道在线),既无法登录也无法传数据。保留它还有好处:SSH 连不上时先 ping 一下,就能判断问题在网络/机器,还是只在 SSH 配置。

查看服务器实际在监听哪些端口
# -t 只看 TCP -l 只看监听 -n 用数字不解析域名 -p 显示进程
sudo ss -tlnp

1.7让服务器自动反击:fail2ban

前面的加固都是被动防守(把门锁牢);fail2ban 让它主动反击:盯着登录日志,发现某 IP 反复失败就自动封禁一段时间——从“守得住”升级为“会还手”。

安装(Rocky 默认源没有,先装 EPEL)
sudo dnf install -y epel-release   # 先装 EPEL 扩展源
sudo dnf install -y fail2ban       # 再装本体

规范做法是在 jail.d/ 下新建 .local 文件,而不是改主配置 jail.conf——软件升级时自己的配置不会被覆盖。

/etc/fail2ban/jail.d/sshd.local
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验证清单与自救

症状真实原因 / 处理
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 自动封禁
  • 进来了不被发现 → 日志审计
  • 三个受用终身的习惯:私钥永不外传、改配置先留后路、先验证再收口
LECTURE 02

网站上线:域名解析与 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域名解析

为什么先做解析:记录添加后要一段时间才全网生效。第一步就做它,让它在后台慢慢生效,同时去做后面的步骤——把需要等待的事提前做,是运维常用思路。

三个字段:

控制台:云解析 DNS → 域名解析 → 解析设置 → 添加记录。加两条 A 记录,主机记录分别填 @ 和 www,记录值都是服务器公网 IP,TTL 默认。

验证解析(自己电脑上执行)
# 看 Address 一栏是不是你的服务器公网 IP
nslookup 你的域名
nslookup www.你的域名

查不到或 IP 不对先别急,可能还在生效,继续往后做,几分钟后再查。

2.3为网站开门(80)

“最小化”不是“什么都不开”,而是只在服务真正上线时才开对应的门。本章只开 80;443 等下一章配证书再开。两层门都要开:

firewalld 放行 HTTP
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 也通了。

准备网站目录

不改系统默认页面,为自己的站点单独建目录,结构清晰,日后加第二站点也互不干扰。

建目录并交给 study 用户
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 也列进怀疑名单。

写站点配置

/etc/nginx/conf.d/mysite.conf
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 模拟不同 Host
curl -H "Host: 你的域名" http://127.0.0.1   # 返回你的主页
curl http://127.0.0.1 | head -5                  # 返回默认测试页

2.6排障实验:亲手制造一个 403

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看见第一位访客

实时看访问日志(Ctrl+C 退出)
sudo tail -f /var/log/nginx/mysite_access.log

对方每刷新一次,屏幕就多一行:来访 IP、时间、访问页面、状态码、浏览器。日志是运维最重要的信息来源——网站出问题,第一件事就是看日志。

2.8验证清单与故障表

现象可能原因排查方法
域名打不开,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 三个环节从前往后查
LECTURE 03

给网站加上安全锁:HTTPS 与证书自动续期

关键词:明文抓包 · CA 与 HTTP-01 · certbot · 301 跳转 · systemd 定时器 + deploy 钩子

一句话

HTTP 是明信片,路上谁都能看;HTTPS 是上了锁的信封。但锁只有 90 天有效期,所以自动续期不是加分项,是必做项。

开始前确认

http://域名 与 http://www.域名 都能看到主页。本章申请证书要用到这两个域名,缺一不可。

3.1先亲眼看看“裸奔”

HTTP 传输的一切都是明文,中间经过的每台设备都能原样看到你访问了什么、提交了什么。装个抓包工具亲自验证:

抓取 80 端口明文流量
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 加密,解决两个问题:

  1. 加密:传输内容变成只有浏览器和服务器能解开的密文;
  2. 身份认证:证明你连的确实是这个域名的真实服务器,而不是冒充者。

身份认证靠数字证书——网站的“身份证”,上面写明属于哪些域名、谁颁发、何时到期。证书必须由 CA(证书颁发机构)签发,浏览器和系统内置了受信任 CA 名单,只有名单上的 CA 签发的证书才会显示安全锁。本章用 Let's Encrypt:免费、全自动的公益 CA。

CA 怎么确认域名是你的

本章用 HTTP-01 验证:

  1. 服务器上运行 certbot,向 Let's Encrypt 申请证书;
  2. Let's Encrypt 给一串随机内容,要求放到 http://域名/.well-known/acme-challenge/随机文件名;
  3. certbot 把文件写进 /var/www/mysite;
  4. Let's Encrypt 从公网访问该地址,读到正确内容 → 证明你控制着这个域名和服务器 → 签发。
反推三个前提

域名必须正确解析到你的服务器;80 端口必须对外开放(验证走 HTTP);Nginx 必须能把验证文件发出去(server_name 与 root 都要对)。这三条也正是申请失败时的三个排查方向。

90 天,别再靠人记

Let's Encrypt 证书有效期只有 90 天,并宣布将在 2028 年 2 月前逐步缩短到 45 天;同时已于 2025 年停止发送到期提醒邮件。靠人记着续期,迟早会忘。

3.3开放 443

照“服务上线才开门”的原则,现在开放 443,两层门都要开:

firewalld 放行 HTTPS
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --list-services     # 应包含 ssh、http、https
80 不能关

一是有人直接输 http:// 时需要 80 把他引导到 HTTPS;二是每次续期 Let's Encrypt 仍要通过 80 验证。关掉 80,三个月后证书就无法续期。

3.4申请证书

安装 certbot(EPEL 第 1 讲已装过)
sudo dnf install -y certbot
certbot --version
先演练,再正式

Let's Encrypt 对失败有次数限制:同一域名一小时内失败太多次会被暂时拒绝。--dry-run 会走完整流程但连接测试环境,既不发证书也不占正式次数。和“改配置先用 -t 测试”是同一个习惯。

演练(把 example.com 和邮箱换成自己的)
# 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”三个方向逐一排查,修好后重跑。

正式申请:去掉 --dry-run
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 有意设置的保护。

用 openssl 读证书内容
# -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

先备份(备份名不能以 .conf 结尾)
sudo cp /etc/nginx/conf.d/mysite.conf /etc/nginx/conf.d/mysite.conf.bak
为什么不能是 .conf

Nginx 会自动加载 conf.d/ 下所有 .conf 文件。备份名若也以 .conf 结尾,会被一并加载,造成重复配置。

现在网站分成两部分:80 只负责两件事——为续期提供验证文件、把其余请求跳转到 HTTPS;443 才真正提供内容。

改写 /etc/nginx/conf.d/mysite.conf(共 6 处 example.com 要换)
# ① 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 精确验证(PowerShell 里要写 curl.exe)
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/ 下的脚本会在每次续期成功后自动执行。

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 的流量长什么样

抓 443 端口(-c 40:抓够 40 个包自动停)
sudo tcpdump -i any -A -nn -c 40 'tcp port 443'

同学访问 https 站点时对比:这次看不到 GET、User-Agent、网页标题,满屏乱码——内容已加密。

但仔细看,可能从乱码里认出你的域名:浏览器建加密连接前要先告诉服务器“我要访问哪个网站”,服务器才能拿出对应证书,这一步是明文的。

记住这句话

HTTPS 隐藏的是你看了什么内容,而不是你访问了哪个网站。

3.8验证清单与故障表

现象可能原因排查方法
certbot 报 DNS problem: NXDOMAIN域名或 www 没有解析记录nslookup 两个域名,补上缺失的 A 记录
certbot 报 Timeout during connect80 端口不通查安全组与 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 天靠自动化而不是靠人记
APPENDIX

把三讲串起来:一条主线、三个习惯

复盘比记命令更重要

讲解决了什么核心命令
第 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,是同一个动作在不同层的重复:先验证,再收口。

三个受用终身的习惯

  1. 私钥永不外传:SSH 私钥、privkey.pem,谁拿到谁就是你。要给别人权限,装他的公钥,不发自己的私钥。
  2. 改配置先留后路:改 sshd 前先走通 VNC,改 Nginx 前先 cp 一份 .bak。
  3. 先验证再收口:密钥能登再禁密码,语法测过再 reload,dry-run 过了再正式申请。

自测:这五个问题能答上来吗