我的运维笔记

Rocky Linux 9 · 服务器运维课程 · 第 1—3 讲听课笔记

第 1 讲 服务器安全加固

目标:把一台"裸奔"的阿里云 ECS(Rocky Linux 9.8,2核2G)加固成只认密钥、守得住、会自动反击的服务器。贯穿全章的三个习惯:私钥永不外传、改配置先留后路、先验证再收口。

1. 为什么要加固

服务器接入公网就会被自动化扫描程序爆破 22 端口。先看证据——查看 SSH 登录失败日志:

sudo journalctl -u sshd | grep -i "failed" | tail -30

会看到大量来自陌生 IP 的失败记录,绝大多数在尝试 root 用户。

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

验证通道:VNC 登录后 whoami 应显示用户名,tty 显示 /dev/tty1(本地控制台);SSH 登录则显示 /dev/pts/0(网络伪终端)。

3. 用密钥代替密码登录

3.1 原理

生成一对配对钥匙:私钥留在自己电脑上绝不外传,是身份的唯一凭证;公钥放到服务器上可公开。服务器用公钥出"数学难题",只有持私钥者能解开。

3.2 准备普通用户

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

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

ssh-keygen -t ed25519 -C "你的名字-服务器加固"

一路回车即可。查看公钥(.pub 结尾的是公钥,另一个是私钥切勿外传):

type $env:USERPROFILE\.ssh\id_ed25519.pub   # Windows PowerShell
cat ~/.ssh/id_ed25519.pub                   # Mac / Linux

3.4 把公钥安装到服务器(重点:权限!)

sudo su - study
mkdir -p ~/.ssh
chmod 700 ~/.ssh                    # 目录仅本人可进入
vi ~/.ssh/authorized_keys           # 粘贴公钥(一整行):i 粘贴 → Esc → :wq
chmod 600 ~/.ssh/authorized_keys    # 文件仅本人可读写
ls -l ~/.ssh                        # 核对:-rw-------,属主是本人
陷阱:权限过松时 SSH 会直接拒绝使用密钥登录且不报错,很多初学者卡在这里。

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

ssh study@服务器公网IP   # 不需要输入密码即为成功
先验证、再收口——顺序反了就会把自己锁在门外。

3.6 为老师配置巡检账号 teacher

绝不能把自己的私钥发给老师!正确做法:老师用自己的密钥对,我们只安装老师的公钥。
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 要输密码,所以单独配免密 sudo:
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 可用"

3.7 加固 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                        # 重新加载生效
sudo firewall-cmd --list-all                      # 核对放行结果
firewalld 改规则必须 --permanent + --reload,否则重启后失效。

读懂安全组规则:ICMP 的端口是"任何"

安全组按"协议+端口"两个维度控制。TCP/UDP 才有端口概念;ICMP 是网络层诊断协议(ping),本身不使用端口,所以端口栏显示 -1/任何只是占位符,并非打开所有端口。保留 ICMP 规则的好处:SSH 连不上时可先 ping 判断问题出在网络还是 SSH 配置。

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

5. 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.conf
[sshd]
enabled = true
backend = systemd    # Rocky 9 关键项:从 systemd 日志读取,别照抄旧教程
maxretry = 3         # 最大失败次数
findtime = 600       # 统计窗口 10 分钟
bantime = 3600       # 封禁 1 小时

连起来就是:10 分钟内失败 3 次,封禁一小时。

sudo systemctl enable --now fail2ban   # 启动并开机自启
sudo fail2ban-client status sshd        # 看 Banned IP list

6. 验证清单

7. 自救速查

本章小结

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

第 2 讲 网站上线:域名解析与 Nginx

目标:为已备案域名配置解析,安装 Nginx,发布带备案号的个人主页。打通三个环节:① DNS 解析 → ② 开放 80 端口 → ③ Nginx 发布网页。

上一章起 SSH 已禁用密码,统一用自己电脑的 SSH 客户端登录;VNC 留作救急后路。若 teacher 免密 sudo 未配置,先补上第 1 讲 3.6 节的命令。

1. DNS:从 IP 到域名

DNS(域名系统)是互联网的"通讯录",把域名翻译成 IP 地址。先做解析是因为 DNS 记录需要时间全网生效——把需要等待的事提前做,是运维常用思路。

解析记录三个字段

字段含义
记录类型A 记录:把域名指向一个 IPv4 地址
主机记录@ 代表域名本身;www 代表 www.域名
TTL记录在各地 DNS 的缓存时长,默认 10 分钟

操作:云解析 DNS → 添加两条 A 记录(@ 和 www,记录值都填服务器公网 IP)。验证:

nslookup 你的域名
nslookup www.你的域名   # Address 应为服务器公网 IP

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

"最小化"不是什么都不开,而是服务上线时才开对应的门。本章只开 80,443 留到下一章。两层门都要开:

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --reload
sudo firewall-cmd --list-services   # 应包含 ssh 和 http

3. 安装 Nginx 并配置站点

sudo dnf install -y nginx
sudo systemctl enable --now nginx   # 立即启动并开机自启
systemctl status nginx               # 应显示 active (running)
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
ls -ld /var/www/mysite                       # 应显示 drwxr-xr-x study study
阿里云 Rocky 镜像默认关闭 SELinux(getenforce 显示 Disabled),本章不涉及;但其他环境遇到 403 时记得 SELinux 也是排查方向。

编写站点配置

sudo vi /etc/nginx/conf.d/mysite.conf
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 靠 HTTP 请求头里的 Host 字段与 server_name 匹配来区分站点。下一章申请证书也靠这一行识别域名。

sudo nginx -t              # 先测语法:syntax is ok
sudo systemctl reload nginx   # 再 reload 生效,不中断访问

4. 发布个人主页

硬性要求:首页底部必须显示 ICP 备案号,并链接到 https://beian.miit.gov.cn/(新窗口打开)。这是合规要求,不是可选项。

上传方式二选一:

scp index.html study@服务器公网IP:/var/www/mysite/   # 方式一:scp 上传,用同一把密钥免密
vi /var/www/mysite/index.html                        # 方式二:直接在服务器编辑

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

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

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

5. 排障实验:403 Forbidden

亲手制造 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
ps -o user= -C nginx | sort -u    # nginx 工作进程以 nginx 用户运行
sudo tail -3 /var/log/nginx/mysite_error.log   # Permission denied
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

原因:读网页文件的是 Nginx 工作进程(nginx 用户),600 权限不允许"其他人"读取。排查思路:先看错误日志,再用 ls -l 核对权限——文件至少 644,目录至少 755。

6. 看见第一位访客

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

每条记录含来访者 IP、时间、页面、状态码、浏览器。网站出问题第一件事就是看日志。

7. 常见故障速查

现象可能原因排查方法
域名打不开,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 启动/重载失败配置语法错误sudo nginx -t 看行号,journalctl -u nginx

本章小结

环节做了什么出问题时的表现
DNS添加 A 记录指向服务器 IP域名打不开,IP 能打开
开门安全组 + firewalld 放行 80连接超时
Nginx安装、配置站点、发布主页403、404 或默认页
合规提醒:网站开通后 30 日内还需在"全国互联网安全管理服务平台"办理公安联网备案,备案号同样显示在网站底部。

第 3 讲 HTTPS 与证书自动续期

目标:为个人主页申请免费 HTTPS 证书,配置自动续期,完成第一个完整作品——带 HTTPS 安全锁的个人主页。

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

HTTP 全部内容明文传输,中间每一台网络设备都能看到。亲手"偷看"自己网站的流量:

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

用手机访问 http 站点后,能看到请求了哪个页面(GET /)、哪个域名(Host:)、什么浏览器(User-Agent),甚至网页标题——表单里的账号密码也一样看得一清二楚。

2. HTTPS 与证书原理

HTTPS = HTTP + TLS 加密,解决两个问题:

身份认证靠数字证书(网站的"身份证"),由受信任的证书颁发机构(CA)签发。本章用 Let's Encrypt——免费、全自动的公益 CA。

CA 如何确认域名是你的(HTTP-01 验证)

  1. 服务器上运行 certbot 向 Let's Encrypt 申请证书;
  2. Let's Encrypt 要求把一串随机内容放到 http://域名/.well-known/acme-challenge/;
  3. certbot 把验证文件写进网站目录;
  4. Let's Encrypt 从互联网访问该地址,读到正确内容即签发证书。

由此推出申请证书的三个前提(也是失败的三个排查方向):①域名正确解析到服务器;②80 端口对外开放;③Nginx 能把验证文件发出去(server_name 和 root 正确)。

Let's Encrypt 证书有效期仅 90 天(将逐步缩短到 45 天),且已停止发到期提醒邮件——自动续期是必须步骤,不是可选项。

3. 开放 443 端口

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,三个月后证书就无法续期。

4. 用 certbot 申请证书

sudo dnf install -y certbot   # EPEL 源(第 1 讲已装)
certbot --version

先演练:--dry-run

Let's Encrypt 对失败申请有次数限制(同域名一小时失败太多次会被暂时拒绝),所以先用测试环境演练,不占正式次数。和"改配置先 -t 测试"是同一个习惯:先演练,再正式。

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 正式申请,看到 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 私钥同理:谁拿到它谁就能冒充你的网站。

sudo openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem \
  -noout -issuer -dates -ext subjectAltName
# 核对:到期时间约 90 天后;两个域名都在列表中

5. 配置 Nginx 启用 HTTPS

sudo cp /etc/nginx/conf.d/mysite.conf /etc/nginx/conf.d/mysite.conf.bak
# 注意备份文件名不能以 .conf 结尾,否则会被 Nginx 一并加载
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;
    }
    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 是 Nginx 变量(访问域名和路径),保证 http://www.example.com/about.html 准确跳到对应的 https 地址,而不是一律跳回首页。

sudo nginx -t
sudo systemctl reload nginx

若 nginx -t 报 cannot load certificate,多半是证书路径中域名写错了。

验证

curl.exe -I http://example.com     # 应 301,Location 指向 https
curl.exe -I https://example.com    # 应 200 OK
curl.exe -I https://www.example.com

浏览器访问 http://域名 会自动变为 https:// 并出现锁图标。PowerShell 里要写 curl.exe,因为 curl 在 PowerShell 中是另一个命令的别名。

6. 配置自动续期

sudo systemctl enable --now certbot-renew.timer   # 每天检查,到期前约 30 天自动续期
systemctl list-timers 'certbot*'                   # NEXT 列是下次检查时间

续期只更新磁盘上的证书文件,Nginx 不会自动读取,须重载生效——用 certbot 的 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
sudo certbot renew --dry-run   # 演练续期,成功即可
演练时会提示跳过 deploy 钩子,是正常现象:演练不产生新证书,自然无需重载 Nginx。

7. 再抓一次包:HTTPS 流量

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

对比 HTTP:这次看不到 GET、User-Agent 和网页标题,全是乱码——内容已加密。但仍能看到你的域名:浏览器建立加密连接前要先告知访问哪个网站(服务器才能拿出对应证书),这一步是明文的。结论:HTTPS 隐藏的是你看了什么内容,而不是你访问了哪个网站。

8. 常见故障速查

现象可能原因排查方法
DNS problem: NXDOMAIN域名或 www 子域名没有解析记录nslookup 核对,补 A 记录
Timeout during connect80 端口不通检查安全组和 firewall-cmd
unauthorized 且 404验证文件没被访问到核对 -w 与 root 一致、server_name 包含该域名
too many failed authorizations一小时内失败过多等一小时;先 --dry-run 演练再正式申请
cannot load certificate证书路径写错对照 certbot certificates 的路径修改
https 访问超时443 端口没开检查安全组和 firewall-cmd
访问 http 没跳转配置未生效或浏览器缓存nginx -t 后 reload;curl.exe -I 直接看响应
锁图标带警告页面引用了 http:// 资源资源地址改为 https:// 或相对路径
改坏配置想恢复—cp 备份文件回去,nginx -t 后 reload

本章小结

步骤做了什么关键点
开门放行 443 端口80 端口不能关:跳转和续期都要用
申请证书certbot 以 webroot 方式申请先 --dry-run 演练,再正式申请
配置 HTTPS80 负责跳转,443 提供内容证书私钥 privkey.pem 绝不外泄
自动续期定时器 + deploy 钩子90 天有效期,靠自动化不靠人记