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