服务器运维 · 模块一

我的运维笔记

从一台"裸奔"的云服务器,到守住门、上得了线、还带安全锁的个人主页。三讲的听课记录,按操作顺序整理,命令可以直接抄。

Rocky Linux 9.8 阿里云 ECS · 2 核 2G 第 1–3 讲 共 3 个实验
第 1 讲

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

目标是把一台"裸奔"的机器,加固成只认密钥、守得住、还会自动反击的服务器。本讲不修改 SSH 端口。

本讲要掌握

  • SSH 密钥认证的原理,配置密钥登录并安全地禁用密码
  • "安全组 + 主机防火墙"两层防护,读懂 ICMP 与端口的关系
  • 用 fail2ban 自动封禁暴力破解者
  • 把自己锁在门外时,用 VNC 完成自救

1.1先看见威胁:22 端口一直被扫描

服务器接入公网的那一刻就不再是无人问津的角落。自动化扫描程序昼夜遍历 IP,一旦发现 22 端口开着,就用海量常见用户名和密码反复尝试——这就是暴力破解。

把证据调出来:只筛登录失败记录,只看最后 30 条
sudo journalctl -u sshd | grep -i "failed" | tail -30

你多半会看到大量陌生 IP 的失败记录,而且绝大多数在尝试 root。所以后面每一步加固,都沿着"攻击者会从哪里进来"这条线索走。

1.2关键认知:登录服务器有两条通道

动手之前必须先分清这两条路。理解了它,才敢放手改配置——因为你知道万一改坏了,还有一条路能进去。

通道走哪里受谁管辖改崩了会怎样
SSH(含控制台 Workbench) 公网 → 安全组 22 端口 → sshd 安全组 + sshd 配置 一起断开,不是后路
VNC 阿里云带外管理通道,相当于坐在物理显示器前 不经过安全组,也不经过 sshd 依然能进,真正能救命的后路
记住 真正能救命的后路是 VNC。禁用密码登录、封掉 22 端口都不影响它,这也是我们敢于大胆加固的底气。

1.3用密钥代替密码

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

  • 私钥:留在自己电脑上,绝不外传,是身份的唯一凭证;
  • 公钥:放到服务器上,可以公开,谁都能看。

登录时服务器用公钥出一道只有对应私钥才解得开的"数学题"。持有私钥的人轻松解开;没有私钥的攻击者,即使拿到公钥也无从下手。

① 先准备一个普通用户

加固后会禁止 root 远程登录,所以先建日常账号。后文的 study 都要换成你自己的用户名。

建用户、设密码、加入 wheel 组(只有 wheel 组成员能用 sudo)
sudo adduser study
sudo passwd study
sudo usermod -aG wheel study

② 先亲自走通一次 VNC

后路只有亲自走通过一次才靠得住。别等真出事了才第一次打开它。在控制台 ECS → 实例 → 远程连接 → VNC 远程连接,用 study 和它的系统密码登录,然后验证身份和终端类型:

用 tty 的结果从系统内部判断"这是哪条通道"
whoami    # 应显示 study
tty       # VNC 登录显示 /dev/tty1;SSH 登录显示 /dev/pts/0 之类

/dev/tty1 是机器本地控制台终端,相当于直接插在服务器上的键盘;/dev/pts/0 是伪终端,代表一条网络连接。两者不同,正好印证了两条通道彼此独立。用完 exit 退出。

VNC 使用要点
  • 粘贴支持差,长命令建议用界面上的"复制命令输入"功能,或手动输入;适合救急,不适合日常操作。
  • 忘记系统密码,可在控制台重置实例密码,重启后生效。
  • 禁用 SSH 密码登录、禁止 root 远程登录,这两项只作用于 SSH,不影响 VNC 用系统密码登录。

③ 在自己电脑上生成密钥对

密钥必须在本机生成,私钥从诞生起就只存在于你的机器里,从不经过网络。Windows 用 PowerShell,Mac/Linux 用终端:

ed25519 算法,现代、短小而安全;一路回车即可(可不设密码短语)
ssh-keygen -t ed25519 -C "你的名字-服务器加固"
查看公钥内容:复制以 ssh-ed25519 AAAA... 开头的完整一行,中间不能换行
# Windows
type $env:USERPROFILE\.ssh\id_ed25519.pub

# Mac / Linux
cat ~/.ssh/id_ed25519.pub

④ 把公钥安装到服务器

所谓"安装公钥",就是把公钥写进该账号的 ~/.ssh/authorized_keys(已授权的公钥清单)。

最容易踩的坑:权限 如果 .ssh 目录或 authorized_keys 权限过于宽松,SSH 会直接拒绝使用它登录,而且不给出任何报错。很多人就在这里卡上半天。下面每一步权限设置都不能省。
目录 700、文件 600,属主必须是你本人
sudo su - study                 # 切换过去,此后 ~ 就是 /home/study
mkdir -p ~/.ssh
chmod 700 ~/.ssh
vi ~/.ssh/authorized_keys       # 按 i 进入编辑 → 粘贴公钥整行 → Esc → :wq 保存
chmod 600 ~/.ssh/authorized_keys
ls -l ~/.ssh                    # 核对:authorized_keys 应为 -rw-------

⑤ 一条铁律:先验证密钥能登录,再往下做

操作纪律 必须先确认新的登录方式(密钥)确实可用,才能去禁用旧的(密码)。顺序反了,就会出现"密码已禁、密钥没配好"、把自己关在门外。正确的运维习惯永远是:先验证、再收口。
能直接登入且不要求输密码,才算配置成功
ssh study@服务器公网IP

如果失败,回控制台查三件事:公钥有没有贴错、权限对不对、用户名对不对。在验证成功之前,绝对不要进行下一步。

⑥ 给老师配一个巡检账号

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

为 teacher 建账号、装老师的公钥、配免密 sudo(teacher 没有密码,wheel 组 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

echo "teacher ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/teacher
sudo chmod 440 /etc/sudoers.d/teacher
sudo visudo -c                                # 全部显示 parsed OK 才算通过
sudo -u teacher sudo -n true && echo "teacher 免密 sudo 可用"
为什么不用 wheel 组 teacher 是专供巡检的账号,没有设置密码;而 Rocky 默认规定 wheel 组成员用 sudo 必须输入本人密码,没有密码可用就执行不了 sudo。所以单独写一条免密规则——这也是巡检、自动化账号的通行做法:只能凭私钥登录,脚本也无法交互输入密码。

务必执行 visudo -c。sudo 配置写错,所有账号的 sudo 都会失效,包括你自己的 study;万一出现,用 VNC 以 root 登录修复。

1.4收口:加固 sshd_config

密钥登录验证通过后,才来关掉不安全的登录方式。

sshd_config 决定谁能登录、用什么方式登录;没有的项就新增
sudo vi /etc/ssh/sshd_config
配置项作用与它防的东西
PubkeyAuthentication yes允许密钥登录,本章加固的基础
PasswordAuthentication no禁止密码登录,杜绝被猜密码(确认密钥能登录后再改)
PermitRootLogin no禁止 root 直接远程登录,缩小攻击面(要管理先登普通用户再 sudo)
MaxAuthTries 3单次连接最多 3 次认证尝试,超过即断开,拖慢爆破
ClientAliveInterval 300每 5 分钟探测一次客户端是否还在
ClientAliveCountMax 2连续 2 次无响应就断开,自动清理挂着的空闲会话
AllowUsers study teacher登录白名单,其余用户一律拒绝(写错用户名或漏掉 teacher 就有人进不来)
改完先验语法,确认无误再重载;reload 不会断开当前连接
sudo sshd -t                  # 没有输出就是正确,有错会指出行号
sudo systemctl reload sshd

至此,服务器已经从"谁都能来猜密码",变成"只有持私钥的白名单用户才能进入"。

1.5两道门:安全组与主机防火墙

云服务器与普通物理机最大的不同,就是防护有两层门,相互独立、都需要配,原则都是同一个词——最小化:只开必须的门。

层管什么
安全组(阿里云平台层)公网流量能不能进入你的实例
主机防火墙 firewalld(系统内)流量到了实例后,放不放行到具体服务
Rocky 上最容易被忽略的细节:必须 --permanent 再 --reload,否则重启后规则失效
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reload
sudo firewall-cmd --list-all

接着到控制台 实例 → 安全组 → 配置规则 检查入方向,把握三点:保留 TCP 22(你自己的 SSH 和 Workbench 都走它,删掉就只剩 VNC);删掉或收紧用不到的端口;ICMP-IPv4 可以保留。

为什么有的规则"端口"是"任何"

安全组按"协议+端口"两个维度控制,并不是只看端口。TCP/UDP 才有端口概念,所以写 TCP 22、TCP 80;ICMP 是网络层诊断协议(ping、traceroute 用的就是它),本身不使用端口,控制台只能在端口栏填 -1/-1 或显示"任何"——那只是占位符,不代表把所有端口都打开了。放行 ICMP 只能让别人 ping 通你(知道它在线),无法登录也无法传输数据。

保留它还有实际好处:日后 SSH 连不上时先 ping 一下,就能判断问题出在网络/机器本身,还是仅仅出在 SSH 配置上。

查看实际监听的端口,与安全组规则相互对照(-t TCP / -l 监听 / -n 数字 / -p 进程)
sudo ss -tlnp

1.6让服务器自动反击:fail2ban

前面的加固都是被动防守——把门锁牢。fail2ban 让服务器学会还手:盯着登录日志,发现某个 IP 反复失败,就自动封禁一段时间。

Rocky 默认源里没有 fail2ban,先装 EPEL 扩展源
sudo dnf install -y epel-release
sudo dnf install -y fail2ban

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

sudo vi /etc/fail2ban/jail.d/sshd.local,填入:
[sshd]
enabled  = true
backend  = systemd
maxretry = 3
findtime = 600
bantime  = 3600

连起来读就是一句话:10 分钟内失败 3 次,就封禁这个 IP 一小时。

Rocky 9 的关键差异 backend = systemd 不能省。新系统的日志由 systemd 统一管理,而网上许多旧教程仍在读 /var/log/secure,那种写法在这里不生效,切勿照抄。
启动并设为开机自启,然后看战果
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd

重点看输出里的 Banned IP list——那就是被你的服务器自动关进"小黑屋"的攻击者。名单暂时是空的也不代表操作有误,只是还没被扫描器盯上,挂一段时间自然会有记录。

1.7验证清单与自救

要确认的事判据
VNC 能登录tty 显示 /dev/tty1
密钥能登录ssh study@公网IP 不需要输密码
密码登录已废用密码被拒绝
root 不能直登root 远程登录被拒
sshd 配置无误sudo sshd -t 无报错
防火墙正确--list-all 里有 ssh,无多余端口
fail2ban 在跑fail2ban-client status sshd 有规则状态
teacher 可巡检老师能凭自己私钥登录;sudo -u teacher sudo -n true 成功

提交方式:只交主机的公网 IP 一样东西。老师用自己的私钥,通过你装好的 teacher 公钥登录检查。任何时候都不要把自己的私钥发给任何人。

症状原因与处置
SSH 连不上用控制台 VNC 进去(不走 SSH,不受 sshd 和安全组影响),改回 sshd_config,再 sshd -t → reload sshd
密钥登录仍要密码99% 是权限:~/.ssh 700、authorized_keys 600,属主是你本人而不是 root
老师连不上多半是 AllowUsers 漏了 teacher,或 teacher 的 .ssh 属主没改回 teacher
sudo 全废VNC 以 root 登录,修正或删除 /etc/sudoers.d/teacher,再 visudo -c
firewalld 改了不生效忘了 --permanent 或忘了 --reload
第 2 讲

让网站上线:域名解析与 Nginx 个人主页

为自己已备案的域名配置解析,装 Nginx,发布一个带备案号的个人主页,让别人输入域名就能访问。

本讲要掌握

  • 说清 DNS 的作用,添加解析记录并验证生效
  • 按需在安全组和防火墙中开放网页访问端口
  • 安装 Nginx,编写站点配置,安全地让配置生效
  • 发布主页,排查 403、404、连接超时等常见故障
开始之前 上一讲已禁用 SSH 密码登录,从本讲起统一用自己电脑的 SSH 客户端登录。控制台 Workbench 需要上传私钥才能登录,不推荐;VNC 留作救急后路。

2.1一个网站能被访问,要打通三个环节

环节做什么出问题时的表现
DNS让域名指向你的服务器 IP域名打不开,IP 能打开
开门安全组 + firewalld 放行 80 端口连接超时
Nginx接收请求并把主页发出去403、404 或看到默认页

今后排查网站故障,就按这三个环节从前往后逐一检查。

2.2域名解析:把 IP 换成名字

DNS 就是互联网的"通讯录":浏览器输入域名后,先向 DNS 查对应的 IP,再去连那台服务器。解析记录添加后需要一段时间才能全网生效,所以第一步就做解析,让它在后台慢慢生效,同时去做后面的步骤——把需要等待的事情提前做。

字段含义
记录类型本讲用 A 记录:把域名指向一个 IPv4 地址
主机记录域名的前缀。@ 代表域名本身,www 代表 www.子域
TTL各地 DNS 的缓存时长,默认 10 分钟——改完最多约 10 分钟才全量更新

在 云解析 DNS → 域名解析 → 解析设置 → 添加记录 里加两条:A @ → 公网IP 和 A www → 公网IP。这样 你的域名 和 www.你的域名 都指向了服务器。

在自己电脑上验证:看 Address 一栏是不是你的公网 IP
nslookup 你的域名
nslookup www.你的域名

暂时查不到也不必着急,可能还在生效中,先继续做后面的步骤,几分钟后再查。

2.3为网站开门:只开 80

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

安全组:实例 → 安全组 → 配置规则 → 入方向 → 添加规则,协议 TCP,端口 80,授权对象 0.0.0.0/0。控制台通常也有"HTTP(80)"的快速添加选项。

主机防火墙放行 HTTP
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --reload
sudo firewall-cmd --list-services    # 应包含 ssh 和 http

2.4安装 Nginx 并配置站点

Nginx 的工作很直接:守在 80 端口,收到浏览器请求后找到对应的网页文件发回去。后面配 HTTPS、做反向代理、当 AI 服务网关都要用它。

安装并设为开机自启,然后确认版本
sudo dnf install -y nginx
sudo systemctl enable --now nginx
systemctl status nginx    # 应显示 active (running),按 q 退出
nginx -v

浏览器访问 http://你的服务器公网IP,看到 Nginx 默认测试页就说明装好了、80 也通了。

不修改系统自带默认页,而是为本站单独建目录,结构清晰,以后加第二个站也不会互相干扰:

目录交给 study,之后上传修改网页不必用 sudo
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        # 应为 drwxr-xr-x,属主 study
关于 SELinux 网上很多"Rocky 网站 403"的讨论都指向 SELinux。阿里云 Rocky 公共镜像默认关闭了它,用 getenforce 确认(应显示 Disabled),所以本讲不涉及。但将来在本地自装的 Rocky 上遇到 403,SELinux 也是一个排查方向。

编写站点配置

Nginx 会自动加载 /etc/nginx/conf.d/ 下所有 .conf 结尾的文件。

sudo vi /etc/nginx/conf.d/mysite.conf,把 example.com 换成你自己的域名
server {
    listen 80;
    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;
    }
}
server_name 是这一行最关键的 一台服务器可以跑很多网站,Nginx 靠浏览器请求头里的 Host 字段区分:请求域名和哪个站点的 server_name 匹配,就交给哪个站点。下一讲申请证书时,证书工具也靠这一行识别你的域名,现在务必写对。
先检查语法,再让配置生效;配置有错直接 reload 可能导致网站中断
sudo nginx -t
sudo systemctl reload nginx

2.5发布个人主页

内容自由发挥,但有一条硬性要求:首页底部必须显示 ICP 备案号,并链接到工信部备案查询网站 https://beian.miit.gov.cn/。这是备案网站的合规要求,不是可选项。

可以让 AI 生成,但要自己检查三件事:备案号是不是你的、链接地址对不对、页面能否正常显示。能讲清页面结构,才算真正完成。

两种上传方式任选:本机写好用 scp(与 ssh 同一把密钥,不用密码),或直接在服务器上编辑
scp index.html study@你的服务器公网IP:/var/www/mysite/

# 或者
vi /var/www/mysite/index.html

完成后访问 http://你的域名 和 http://www.你的域名,都应该能看到你的主页。

为什么用 IP 访问看到的是默认页

用域名看到主页,用 IP 看到默认页——这正是 server_name 在起作用。用 IP 访问时请求里的 Host 不是你的域名,匹配不上你的站点,于是交给系统默认站点处理。可以在服务器上用 curl 手动指定 Host 亲眼验证:

-H 手动指定请求头 Host;127.0.0.1 表示访问本机
curl -H "Host: 你的域名" http://127.0.0.1     # 返回你的主页内容
curl http://127.0.0.1 | head -5              # 不带域名 → 默认测试页

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

403 Forbidden 最常见的原因是文件权限不对。我们制造一次,再通过日志找到原因、修好它。

建测试页,把权限改成 600,然后只看响应头
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 Forbidden

文件明明属于 study,为什么 Nginx 读不了?因为 Nginx 不是以 study 的身份运行的:

查证:真正读文件的是 nginx 工作进程,错误日志直接说了原因
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。放宽权限即可:

644 后应返回 200 OK;实验结束删掉测试文件
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
403 排查思路 先看错误日志找原因,再用 ls -l 核对权限。网页文件至少 644,目录至少 755,Nginx 才能正常读取。

2.7看见你的第一位访客

tail -f 持续显示新增内容,按 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 启动/重载失败配置语法错误nginx -t 看行号;journalctl -u nginx
合规提醒 除 ICP 备案外,网站开通后 30 日内还需在"全国互联网安全管理服务平台"办理公安联网备案,取得公安备案号后同样要显示在网站底部。具体要求以当地规定为准。
第 3 讲

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

为上一章的主页申请免费证书,让它从"不安全"变成带锁的加密站,并配置到期前自动续期。这一讲完成后,第一个完整作品就交付了。

本讲要掌握

  • 说清 HTTP 明文传输的风险,以及 HTTPS 加密了什么、没加密什么
  • 理解数字证书的作用,以及 CA 如何确认域名属于你
  • 用 certbot 申请证书,编写 Nginx 的 HTTPS 配置
  • 配置证书自动续期,并验证它确实可用
前置条件 上一章成果必须在:http://你的域名 和 http://www.你的域名 都能看到主页。本章申请证书要用这两个域名,缺一不可。

3.1你的网站正在"裸奔"

访问 http:// 时浏览器标注"不安全",不是危言耸听:HTTP 传输的所有内容都是明文,中间经过的每一台网络设备都能原样看到。我们亲手"偷看"一次自己网站的流量。

安装 tcpdump 并抓 80 端口,只筛出请求行、域名、浏览器信息和网页标题
sudo dnf install -y tcpdump

sudo tcpdump -i any -A -nn 'tcp port 80' | grep -a -E "GET |Host:|User-Agent|<title>"

请同学用手机访问你的站点,屏幕上会直接出现:请求了哪个页面(GET /)、访问的是哪个域名(Host:)、用的什么手机和浏览器(User-Agent),甚至网页标题。如果页面里有登录表单,账号密码也会被看得一清二楚。按 Ctrl+C 停止,最后我们会再抓一次做对比。

3.2HTTPS 与证书的原理

HTTPS 就是 HTTP 加上 TLS 加密,它解决两个问题:

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

身份认证靠数字证书——网站的"身份证",上面写明这张证书属于哪些域名、由谁颁发、什么时候到期。身份证要公安机关签发才可信,证书也要由 CA(证书颁发机构) 签发;浏览器和操作系统内置了一份受信任 CA 名单,只有名单上的 CA 签发的证书,浏览器才显示安全锁。本章用 Let's Encrypt,一个免费、全自动的公益 CA。

CA 怎么确认域名是你的:HTTP-01 验证

  1. 你在服务器上运行 certbot,向 Let's Encrypt 申请证书;
  2. Let's Encrypt 交给 certbot 一串随机内容,要求放到 http://你的域名/.well-known/acme-challenge/一串随机文件名;
  3. certbot 把这个文件写进网站目录 /var/www/mysite;
  4. Let's Encrypt 从互联网访问这个地址,读到正确内容就证明你控制着这个域名和服务器,于是签发证书。
由此推出的三个前提(也是失败时的三个排查方向)
  1. 域名必须正确解析到你的服务器(上一章的 A 记录);
  2. 80 端口必须对外开放(Let's Encrypt 通过 HTTP 验证);
  3. Nginx 必须能把验证文件发出去(server_name 和 root 都要对)。
有效期只有 90 天,而且没人提醒你 Let's Encrypt 证书有效期 90 天,并已宣布将在 2028 年 2 月前逐步缩短到 45 天;同时它已于 2025 年停止发送到期提醒邮件。也就是说,靠人记着续期,迟早会忘——自动续期是必须完成的步骤,不是可选项。

3.3开放 443 端口

安全组:入方向添加 TCP 443,授权对象 0.0.0.0/0(控制台通常有"HTTPS(443)"快速选项)。

主机防火墙放行 https,核对应包含 ssh、http、https
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --list-services
80 端口不能关 两个理由:① 有人直接输 http:// 访问时,需要 80 把他引导到 HTTPS;② 证书每次续期,Let's Encrypt 仍要通过 80 端口验证。关掉 80,三个月后证书就无法续期。

3.4申请证书

certbot 是 Let's Encrypt 官方推荐工具,位于 EPEL 扩展源中(第 1 讲装 fail2ban 时已装过 EPEL)。

安装并确认版本
sudo dnf install -y certbot
certbot --version
先演练,再正式 Let's Encrypt 对失败次数有限制:同一域名一小时内验证失败太多次会被暂时拒绝。所以先用 --dry-run 走完整流程,但连接测试环境,不真签发也不占用正式次数。这和前两讲"改配置先 -t 测试"是同一个习惯。
演练:看到 The dry run was successful. 才算通过
sudo certbot certonly --webroot -w /var/www/mysite \
  -d example.com -d www.example.com \
  -m 你的邮箱 --agree-tos --no-eff-email --dry-run

行尾反斜杠表示"命令没写完,下一行接着写",三行合起来是一条命令。失败就按"解析、80 端口、Nginx"三个方向逐一检查,修好后再演练一次。演练成功,去掉 --dry-run 正式申请,看到 Successfully received certificate. 即签发成功。

看看你的证书:包含哪些域名、到期时间、剩余天数、文件位置
sudo certbot certificates
sudo ls -l /etc/letsencrypt/live/example.com/
文件内容用途
fullchain.pem你的证书 + 中间证书发给访问者,证明网站身份
privkey.pem证书私钥留在服务器上用于解密,绝不能泄露
cert.pem仅你的证书本章不直接使用
chain.pem仅中间证书本章不直接使用

privkey.pem 与第 1 讲的 SSH 私钥同一个道理:谁拿到它,谁就能冒充你的网站。它所在目录只有 root 能访问,这是 certbot 有意设置的。

查看证书详情,然后自己核对两件事:到期时间是否约 90 天后?两个域名是否都在?
sudo openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem \
  -noout -issuer -dates -ext subjectAltName

3.5配置 Nginx 启用 HTTPS

先备份。注意备份文件名不能以 .conf 结尾,否则会被 Nginx 一并加载,造成重复配置。

备份现有配置
sudo cp /etc/nginx/conf.d/mysite.conf /etc/nginx/conf.d/mysite.conf.bak

现在网站分成两部分:80 端口不再直接提供网页,只负责两件事——给续期提供验证文件,把其余请求跳转到 HTTPS;443 端口才真正提供内容。

sudo vi /etc/nginx/conf.d/mysite.conf,全部替换;example.com 共 6 处要改(两行 server_name 各 2 处,两行证书路径各 1 处)
# ① 80 端口:只做验证 + 跳转
server {
    listen 80;
    server_name example.com www.example.com;

    location /.well-known/acme-challenge/ {
        root /var/www/mysite;              # 续期时 Let's Encrypt 仍通过 HTTP 读这里
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

# ② 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,而不是一律跳回首页。

测试并生效;报 cannot load certificate 多半是证书路径里的域名写错,对照 certbot certificates 核对
sudo nginx -t
sudo systemctl reload nginx

浏览器访问 http://你的域名,地址会自动变成 https://,出现锁图标。点开可看颁发者和有效期,应与 3.4 查到的一致。也可以在本机精确验证(Windows PowerShell 里要写 curl.exe,因为 curl 是另一个命令的别名):

301 是跳转正确,200 是内容正常
curl.exe -I http://example.com          # 301 Moved Permanently,Location 指向 https://
curl.exe -I https://example.com         # 200 OK
curl.exe -I https://www.example.com     # 200 OK

3.6配置自动续期

certbot 自带 systemd 定时器 certbot-renew.timer,每天检查、到期前约 30 天自动续期。安装后它不一定已启用,要主动启用并确认。

启用定时器并查看下次检查时间(NEXT 一列)
sudo systemctl enable --now certbot-renew.timer
systemctl list-timers 'certbot*'

续期只更新磁盘上的证书文件,Nginx 不会自动读新证书,必须重载一次。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
演练续期:看到 Congratulations, all simulated renewals succeeded 说明流程通了
sudo certbot renew --dry-run
演练时提示跳过了 deploy 钩子,是正常的 演练不会真的产生新证书,自然也不需要重载 Nginx。钩子脚本本身我们已在上面手动验证过了。

3.7再抓一次包:HTTPS 的流量长什么样

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

请同学用手机访问 https://你的域名,与 3.1 对比:看不到 GET、User-Agent,也看不到网页标题,屏幕上全是乱码。

但域名还在 仔细看,乱码里仍能找到你的域名——因为浏览器在建立加密连接前,要先告诉服务器"我要访问哪个网站",服务器才能拿出对应证书,而这一步是明文进行的。所以请记住:HTTPS 隐藏的是你看了什么内容,而不是你访问了哪个网站。

3.8验证清单与故障速查

要确认的事判据
端口安全组和 firewalld 都放行 443,且 80 仍然开放
证书certbot certificates 显示两个域名,剩余约 89 天
配置sudo nginx -t 通过
浏览器http:// 自动跳 https://,地址栏有锁
命令行http 返回 301,https 与 https://www 都返回 200
续期定时器可见;certbot renew --dry-run 成功
备案号主页底部备案号仍正常显示
现象可能原因排查方法
DNS problem: NXDOMAIN域名或 www 子域没有解析记录nslookup 两个域名,补上缺失的 A 记录
Timeout during connect80 端口不通查安全组和 firewall-cmd --list-services
unauthorized 且带 404验证文件访问不到核对 -w 是否与 Nginx 的 root 一致、server_name 是否含该域名
too many failed authorizations一小时内失败次数过多等一小时;之后先用 --dry-run 成功再正式申请
cannot load certificate证书路径写错对照 certbot certificates 的路径修改
https 访问超时443 端口没开查安全组和 firewalld
访问 http 没跳转配置未生效或浏览器缓存nginx -t 后 reload;用 curl.exe -I 看响应
锁图标带警告页面引用了 http:// 的图片或脚本改成 https:// 或相对路径
改坏了想恢复—cp mysite.conf.bak mysite.conf,再 nginx -t 和 reload

★ 作品一完成:带 HTTPS 安全锁的个人主页。本讲无需提交新材料,老师会统一检查你的 https://域名。

贯穿三讲

三个受用终身的运维习惯

技术会忘,习惯不会。这三条比任何一条命令都重要。

01
私钥永不外传

SSH 私钥、证书私钥 privkey.pem 都是身份的唯一凭证。要给老师权限,装的是对方的公钥。

02
改配置先留后路

动 sshd 之前先走通 VNC,改 Nginx 之前先 cp 一份 .bak。有退路才敢大胆操作。

03
先验证再收口

sshd -t、nginx -t、--dry-run、renew --dry-run——顺序一旦反了,就可能把自己关在门外。

一句话串起来 看见威胁 → 锁门(密钥 + 白名单)→ 只留一扇门(firewalld + 安全组)→ 会还手(fail2ban)→ 挂上域名(DNS + Nginx)→ 加锁(HTTPS + 自动续期)。一条从"能登录"到"能交付"的完整链路。