我的运维笔记

《服务器运维》模块一 · Rocky Linux 9 / 阿里云 ECS 听课笔记

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

目标:把一台"裸奔"的阿里云 ECS(Rocky Linux 9.8,2核2G)加固成只认密钥、守得住、还会自动反击的服务器。本章不涉及修改 SSH 端口。

贯穿全章的三个职业习惯:私钥永不外传、改配置先留后路、先验证再收口。

1. 为什么加固:服务器正在被扫描

服务器接入公网后会被自动化程序昼夜扫描,发现 22 端口开放就用海量用户名/密码组合尝试登录(暴力破解)。先调证据看看:

# journalctl -u sshd:查看 SSH 服务的系统日志
# grep -i "failed":筛出登录失败记录(-i 忽略大小写);tail -30:只看最后 30 条
sudo journalctl -u sshd | grep -i "failed" | tail -30

大概率能看到大量陌生 IP 的失败记录,绝大多数在尝试 root —— 这就是攻击者。

2. 登录服务器的两条通道

通道路径特点
SSH(含 Workbench)走公网 → 安全组 22 端口 → sshd改崩 SSH 配置或封了 22 端口就断,不是后路
VNC阿里云带外管理通道,模拟物理显示器不经过安全组也不经过 sshd,改坏任何东西都能进去自救
记住:真正能救命的后路是 VNC。VNC 登录后 tty 显示 /dev/tty1(本地控制台),SSH 登录显示 /dev/pts/0(网络伪终端),正好印证两条通道独立。

3. 用密钥代替密码登录

3.1 原理

3.2 准备普通用户(把 study 换成自己的用户名)

sudo adduser study          # 新建普通用户
sudo passwd study           # 设置登录密码
sudo usermod -aG wheel study # 加入 wheel 组,才能用 sudo

3.3 动手前先走一遍 VNC 后路

控制台 → ECS 实例 → 远程连接 → VNC 登录,验证 whoami 和 tty。密码不显示字符是正常现象。忘记密码可在控制台"重置实例密码"。

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

# -t ed25519:现代、短小而安全的算法;-C:备注方便辨认
ssh-keygen -t ed25519 -C "你的名字-服务器加固"

查看公钥(.pub 结尾):type $env:USERPROFILE\.ssh\id_ed25519.pub(Windows)或 cat ~/.ssh/id_ed25519.pub(Mac/Linux)。

3.5 把公钥安装到服务器(最容易踩权限坑)

sudo su - study                    # 切换到 study 用户
mkdir -p ~/.ssh                    # 创建 .ssh 目录
chmod 700 ~/.ssh                   # 目录 700:仅本人可访问
vi ~/.ssh/authorized_keys          # 粘贴公钥(一整行):i 粘贴 → Esc → :wq
chmod 600 ~/.ssh/authorized_keys   # 文件 600:仅本人可读写
ls -l ~/.ssh                       # 核对:-rw------- 且属主是本人
权限过松时 SSH 会直接拒绝用该文件登录且不报错——密钥登录失败 99% 是权限问题。

3.6 铁律:先验证密钥能登录,再禁用密码

ssh study@服务器公网IP   # 不需要输入密码即成功;失败绝不做下一步

3.7 给老师配巡检账号(不发给老师你的私钥!)

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 可用"

3.8 加固 sshd 配置

sudo vi /etc/ssh/sshd_config
PubkeyAuthentication yes      # 允许密钥登录(基础)
PasswordAuthentication no     # 禁止密码登录(务必先验证密钥可用)
PermitRootLogin no            # 禁止 root 远程登录,缩小攻击面
MaxAuthTries 3                # 单连接最多 3 次认证,拖慢爆破
ClientAliveInterval 300       # 每 5 分钟探测客户端
ClientAliveCountMax 2         # 连续 2 次无响应即断开,清理空闲会话
AllowUsers study teacher      # 登录白名单,写错用户名对方就被挡在门外
sudo sshd -t                  # 先测语法:无输出即正确
sudo systemctl reload sshd    # reload 不断开当前连接,比 restart 安全

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

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

sudo firewall-cmd --permanent --add-service=ssh  # 永久放行 SSH
sudo firewall-cmd --reload                       # 不加 --permanent 重启就失效!
sudo firewall-cmd --list-all                     # 核对结果

读懂安全组里的 ICMP 规则

ICMP 是网络层诊断协议(ping、traceroute),本身不使用端口,控制台里端口显示 -1/"任何"只是占位符,不代表打开所有端口。放行 ICMP 只能让别人 ping 通你,不能登录、不能传数据。保留它还有好处:SSH 连不上时先 ping 一下,就能区分是网络问题还是 SSH 配置问题。

sudo ss -tlnp   # -t TCP -l 监听中 -n 数字显示 -p 显示进程,与安全组对照

5. fail2ban:让服务器自动反击

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

规范做法:在 jail.d/ 下写 .local 文件,不直接改 jail.conf(升级不被覆盖)。Rocky 9 必须写 backend = systemd(日志由 systemd 管理,读 /var/log/secure 的旧教程写法在这里不生效):

sudo vi /etc/fail2ban/jail.d/sshd.local
[sshd]
enabled = true
backend = systemd    # Rocky 9 的正确方式
maxretry = 3         # 10 分钟内失败 3 次
findtime = 600       # 统计窗口 600 秒
bantime = 3600       # 封禁 1 小时
sudo systemctl enable --now fail2ban   # 启动并设开机自启
sudo fail2ban-client status sshd       # 看 Banned IP list
封禁名单暂时为空不代表操作有误,只是还没被扫描器盯上。

6. 自救指南

本章小结

攻击者想做的事你部署的加固
猜密码登进来SSH 密钥登录 + 禁用密码 + 禁止 root
从别的端口钻进来firewalld + 安全组,两层最小化放行
反复爆破 SSHfail2ban 自动封禁
进来了不被发现journal 日志审计

第 2 讲让你的网站上线:域名解析与 Nginx 个人主页

目标:为已备案域名配置解析,安装 Nginx,发布带备案号的个人主页。从本章起统一用自己的 SSH 客户端登录(Workbench 需上传私钥不推荐;VNC 留作救急)。

补做一步:为 teacher 补免密 sudo 规则(同第 1 讲 3.7 节的 echo "teacher ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/teacher 等命令)。

1. 从 IP 到域名:网站上线三环节

  1. DNS:让域名指向服务器 IP(DNS 是互联网的"通讯录");
  2. 开门:安全组和防火墙放行 80 端口;
  3. Nginx:运行 Web 服务器,接收请求并把主页发出去。

2. 域名解析

为什么先做解析?DNS 记录要一段时间才全网生效——把需要等待的事提前做,是运维常用思路。

三个字段:记录类型 A 记录 = 域名指向 IPv4 地址;主机记录 @ = 域名本身,www = www 子域名;TTL = 各地 DNS 缓存时长(默认 10 分钟)。

在云解析 DNS 里添加两条 A 记录:@ 和 www,记录值都是服务器公网 IP。

nslookup 你的域名        # 验证解析,Address 应为服务器公网 IP
nslookup www.你的域名

3. 为网站开门(80 端口)

"最小化"不是"什么都不开",而是服务上线时才开对应的门。本章只开 80;443 等第 3 讲配证书时再开。

安全组:入方向添加规则,协议 TCP、端口 80、授权对象 0.0.0.0/0(任何来源)。

sudo firewall-cmd --permanent --add-service=http  # 放行 HTTP(TCP 80)
sudo firewall-cmd --reload
sudo firewall-cmd --list-services                 # 应包含 ssh 和 http

4. 安装 Nginx 并配置站点

sudo dnf install -y nginx           # 安装 Nginx
sudo systemctl enable --now nginx   # 启动并设开机自启
systemctl status nginx              # 应显示 active (running),按 q 退出
nginx -v                            # 查看版本

浏览器访问 http://公网IP 看到 Nginx 默认测试页即成功。

准备网站目录

sudo mkdir -p /var/www/mysite           # 单独建目录,不碰系统默认页
sudo chown -R study:study /var/www/mysite # 交给 study,之后不用 sudo
sudo chmod 755 /var/www/mysite          # 属主可读写,其他人(含 Nginx)可进入
getenforce                              # SELinux 状态;阿里云 Rocky 镜像默认 Disabled

编写站点配置(关键:server_name)

sudo vi /etc/nginx/conf.d/mysite.conf
server {
    listen 80;
    server_name example.com www.example.com;   # 靠 Host 请求头匹配站点,务必写对
    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;
    }
}
sudo nginx -t                 # 先测语法(同 sshd -t 的习惯)
sudo systemctl reload nginx   # reload 不中断正在进行的访问

5. 发布个人主页

硬性合规要求:首页底部必须显示 ICP 备案号,并链接到工信部备案查询网站 https://beian.miit.gov.cn/ 。这不是可选项。另外网站开通后 30 日内还需办理公安联网备案。

上传方式二选一:

# 方式一:本地写好,scp 上传(用同一把密钥,不需要密码)
scp index.html study@你的服务器公网IP:/var/www/mysite/
# 方式二:直接在服务器上编辑(目录已属 study,不需要 sudo)
vi /var/www/mysite/index.html

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

用 IP 访问时请求的 Host 不是你的域名,匹配不上你的站点,被交给系统默认站点处理。可亲自验证:

curl -H "Host: 你的域名" http://127.0.0.1  # Host 是你的域名 → 返回你的主页
curl http://127.0.0.1 | head -5             # 不带域名 → 返回默认测试页

6. 排障实验:亲手制造并修复一次 403

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

ps -o user= -C nginx | sort -u       # 查 Nginx 进程身份:root 主进程 + nginx 工作进程
sudo tail -3 /var/log/nginx/mysite_error.log  # 日志里有 (13: Permission denied)

chmod 644 /var/www/mysite/test.html  # 放宽为 644
curl -I -H "Host: 你的域名" http://127.0.0.1/test.html     # 200 OK
rm /var/www/mysite/test.html
403 的本质:真正读网页文件的是 nginx 工作进程(身份是 nginx 用户),不是 study。排查思路:先看错误日志,再用 ls -l 核对权限。网页文件至少 644,目录至少 755。

7. 看见第一位访客

sudo tail -f /var/log/nginx/mysite_access.log   # 实时查看访问记录,Ctrl+C 退出

日志记录来访 IP、时间、页面、状态码和浏览器。日志是运维最重要的信息来源,网站出问题第一件事就是看日志。

8. 常见故障速查

现象可能原因排查方法
域名打不开,IP 能打开解析未生效或记录填错nslookup 查看;等 TTL 后再试
连接超时80 端口没开检查安全组 + firewall-cmd --list-services
403 Forbidden文件/目录权限不足先看错误日志;文件 644、目录 755
404 Not Found文件名或路径不对确认 index.html 和 root 路径
域名访问仍是默认页server_name 写错或漏 www检查配置,nginx -t 后 reload
Nginx 启动/重载失败配置语法错误nginx -t 看行号;journalctl -u nginx

本章小结

环节本章做了什么出问题时的表现
DNS添加 A 记录,域名指向服务器 IP域名打不开,IP 能打开
开门安全组 + firewalld 放行 80 端口连接超时
Nginx安装、配置站点、发布主页403、404 或看到默认页
今后排查网站故障,就按这三个环节从前往后逐一检查。

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

目标:为个人主页申请免费 HTTPS 证书,配置到期自动续期,完成第一个完整作品——带安全锁的 HTTPS 个人主页。前提:上一章 http://域名 和 http://www.域名 都能访问(申请证书缺一不可)。

1. HTTP 正在"裸奔":抓包实验

HTTP 全部明文传输,中间的每一台网络设备都能看到你访问的页面和提交的内容:

sudo dnf install -y tcpdump   # 安装抓包工具
# -i any 所有网卡 -A 文本显示 -nn 数字显示,只抓 80 端口流量
sudo tcpdump -i any -A -nn 'tcp port 80' | grep -a -E "GET |Host:|User-Agent|<title>"

别人用手机访问时,屏幕上能看到请求的页面(GET /)、域名(Host:)、浏览器(User-Agent)甚至网页标题。若有登录表单,账号密码也看得一清二楚。

2. HTTPS 与证书原理

证书由证书颁发机构(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 端口对外开放;③ Nginx 的 server_name 和 root 正确。
Let's Encrypt 证书有效期只有 90 天(2028 年 2 月前逐步缩短到 45 天),且 2025 年起停止发送到期提醒邮件。自动续期是必须步骤,不是可选项。

3. 开放 443 端口

安全组:TCP 443,授权对象 0.0.0.0/0。主机防火墙:

sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --list-services   # 应包含 ssh、http、https
80 端口不能关!原因:① 有人输入 http:// 时需要 80 引导跳转到 HTTPS;② 证书每次续期仍要通过 80 验证。关掉 80,三个月后证书就无法续期。

4. 用 certbot 申请证书

sudo dnf install -y certbot   # 在 EPEL 源中(第 1 讲装 fail2ban 时已装 EPEL)
certbot --version
先演练再正式:Let's Encrypt 对失败申请有次数限制(同域名一小时内失败太多会被暂时拒绝)。--dry-run 走完整流程但用测试环境,不占正式次数——和前两章"先用 -t 测试"是同一个习惯。
# certonly:只申请证书,不自动改 Nginx 配置(配置自己写才看得懂)
# --webroot:用网站目录方式验证;-w 指定网站目录;-d 指定域名(带 www 和不带各一个)
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 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 域名
sudo openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem \
  -noout -issuer -dates -ext subjectAltName                # 颁发者/有效期/域名列表
文件内容用途
fullchain.pem你的证书 + 中间证书发给访问者,证明网站身份
privkey.pem证书私钥留在服务器解密用,绝不能泄露(与 SSH 私钥同理:谁拿到谁就能冒充你的网站)
cert.pem仅你的证书本章不直接使用
chain.pem仅中间证书本章不直接使用

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
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;   # 其余请求永久跳转到同一地址的 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 是 Nginx 变量,代表访问者请求的域名和路径,所以跳转能保留原路径而不是一律跳回首页。配置里 example.com 共 6 处要替换。报 cannot load certificate 多半是证书路径域名写错。
sudo nginx -t
sudo systemctl reload nginx

验证

curl.exe -I http://example.com    # 应返回 301,Location 指向 https://
curl.exe -I https://example.com   # 应返回 200 OK
curl.exe -I https://www.example.com  # 同样 200 OK
PowerShell 里要写 curl.exe,因为 PowerShell 的 curl 是另一个命令的别名。浏览器访问 http://域名 应自动变 https:// 且地址栏有锁图标。

6. 配置自动续期

certbot 自带 systemd 定时器 certbot-renew.timer,每天检查、到期前约 30 天自动续期:

sudo systemctl enable --now certbot-renew.timer   # 启用定时器
systemctl list-timers 'certbot*'                  # NEXT 列是下次检查时间

续期只更新磁盘上的证书文件,Nginx 必须重载才生效——用 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                                         # 演练一次续期
演练会提示跳过 deploy 钩子,属正常现象(演练不产生新证书)。看到 Congratulations, all simulated renewals succeeded 即流程通畅。

7. 再抓包对比:HTTPS 的流量长什么样

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

对比:看不到 GET、User-Agent、网页标题,全是乱码——内容已加密。但仍可能看到域名:建立加密连接前浏览器要先明文告诉服务器访问哪个网站,服务器才能拿出对应证书。

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

8. 常见故障速查

现象可能原因排查方法
certbot 报 DNS problem: NXDOMAIN域名或 www 没有解析记录nslookup 核对,补 A 记录
certbot 报 Timeout during connect80 端口不通检查安全组和 firewalld
certbot 报 unauthorized 且 404验证文件没被访问到核对 -w 与 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:// 或相对路径
配置改坏了想恢复—用 .bak 覆盖回去,nginx -t 后 reload

本章小结

步骤做了什么关键点
开门放行 443 端口80 端口不能关:跳转和续期都要用
申请证书certbot 以 webroot 方式申请先 --dry-run 演练,再正式申请
配置 HTTPS80 负责跳转,443 提供内容私钥 privkey.pem 绝不外泄
自动续期定时器 + deploy 钩子90 天有效期,靠自动化不靠人记
★ 作品一完成:带 HTTPS 安全锁的个人主页——域名、备案号、安全锁齐备的完整规范网站。