我的运维笔记

《服务器运维》课程学习记录 · Rocky Linux 9 · 阿里云 ECS

第 1 讲 · 服务器安全加固让服务器只认密钥、守得住、还会自动反击

1.1 看见威胁:服务器正在被扫描

公网上的扫描程序昼夜不停地尝试登录 22 端口(暴力破解)。查看证据:

# 查看 SSH 登录失败记录(最后 30 条)
sudo journalctl -u sshd | grep -i "failed" | tail -30

1.2 登录的两条通道

1.3 用密钥代替密码

私钥留在自己电脑上永不外传;公钥放到服务器上可以公开。先建普通用户:

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

在自己电脑上生成密钥对并查看公钥:

ssh-keygen -t ed25519 -C "你的名字-服务器加固"
type $env:USERPROFILE\.ssh\id_ed25519.pub   # Windows 查看公钥

把公钥装进服务器的 ~/.ssh/authorized_keys,权限是最大的坑:

chmod 700 ~/.ssh                       # 目录仅本人可进入
chmod 600 ~/.ssh/authorized_keys       # 文件仅本人可读写,权限过松 SSH 会直接拒绝使用
铁律:先验证密钥能登录,再禁用密码。顺序反了会把自己锁在门外。

1.4 为老师配置巡检账号

绝不能把自己的私钥发给老师;正确做法是安装老师的公钥。teacher 没有密码,需单独配免密 sudo:

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

1.5 加固 sshd

sudo vi /etc/ssh/sshd_config
# 关键项:
# PubkeyAuthentication yes    允许密钥登录
# PasswordAuthentication no   禁止密码登录(先验证密钥再改!)
# PermitRootLogin no          禁止 root 直接远程登录
# MaxAuthTries 3              单连接最多 3 次认证
# AllowUsers study teacher    登录白名单

sudo sshd -t               # 先测语法,无输出即正确
sudo systemctl reload sshd # 再 reload 生效(不断开当前连接)

1.6 两道门:安全组 + firewalld

安全组是云平台层的门,firewalld 是系统内的门,两层都要按最小化原则配置。ICMP 规则端口显示"任何"不是漏洞——ICMP 本身不使用端口,只是占位符,保留它方便 ping 诊断。

sudo firewall-cmd --permanent --add-service=ssh  # 必须加 --permanent,否则重启失效
sudo firewall-cmd --reload
sudo firewall-cmd --list-all                     # 核对放行结果
sudo ss -tlnp                                    # 查看本机实际监听的端口

1.7 fail2ban 自动封禁爆破者

sudo dnf install -y epel-release   # Rocky 默认源没有 fail2ban,先装 EPEL
sudo dnf install -y fail2ban
sudo vi /etc/fail2ban/jail.d/sshd.local
# 写入(.local 文件升级时不被覆盖;Rocky 9 必须 backend=systemd,别抄旧教程读日志文件的写法):
# [sshd]
# enabled = true
# backend = systemd
# maxretry = 3      10 分钟内失败 3 次
# findtime = 600
# bantime = 3600    封禁 1 小时
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd    # 看 Banned IP list

1.8 小结

攻击者想做的事部署的加固
猜密码登进来SSH 密钥登录 + 禁用密码 + 禁止 root
从别的端口钻进来firewalld + 安全组,两层最小化放行
反复爆破 SSHfail2ban 自动封禁
进来了不被发现journal 日志审计
三个受用终身的习惯:私钥永不外传、改配置先留后路(VNC)、先验证再收口。

第 2 讲 · 网站上线域名解析与 Nginx 个人主页

2.1 域名解析(先做,等它后台生效)

DNS 是互联网的"通讯录",把域名翻译成 IP。在阿里云云解析添加两条 A 记录:主机记录分别填 @(域名本身)和 www,记录值为服务器公网 IP,TTL 默认 10 分钟。

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

2.2 为网站开门:开放 80 端口

两层门都要开(安全组添加 TCP 80,授权对象 0.0.0.0/0),"最小化"是服务上线时才开对应的门:

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

2.3 安装并启动 Nginx

sudo dnf install -y nginx
sudo systemctl enable --now nginx    # 立即启动 + 开机自启
systemctl status nginx               # 应显示 active (running)
nginx -v

2.4 准备网站目录

sudo mkdir -p /var/www/mysite
sudo chown -R study:study /var/www/mysite   # 交给 study,之后不必用 sudo
sudo chmod 755 /var/www/mysite              # 目录至少 755,Nginx 才能进入读取
ls -ld /var/www/mysite

2.5 编写站点配置(一站一文件)

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               # 改配置先测试,通过再生效
sudo systemctl reload nginx

2.6 发布主页与上传

scp index.html study@你的服务器公网IP:/var/www/mysite/   # 用密钥,不需要密码

用 IP 访问看到的是 Nginx 默认页,用域名看到的才是主页——因为 IP 访问的 Host 匹配不上 server_name,被交给默认站点处理。可亲自验证:

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

2.7 排障实验:亲手制造 403

echo "<h1>test</h1>" > /var/www/mysite/test.html
chmod 600 /var/www/mysite/test.html                          # 制造 403
curl -I -H "Host: 你的域名" http://127.0.0.1/test.html        # 403 Forbidden
ps -o user= -C nginx | sort -u                               # worker 进程以 nginx 用户运行
sudo tail -3 /var/log/nginx/mysite_error.log                 # Permission denied
chmod 644 /var/www/mysite/test.html                          # 修复:文件至少 644
排查 403 的思路:先看错误日志找原因,再用 ls -l 核对权限。文件至少 644、目录至少 755。真正读网页的是 nginx 用户(工作进程),不是 study。

2.8 看见访客

sudo tail -f /var/log/nginx/mysite_access.log   # Ctrl+C 退出;日志是运维最重要的信息来源

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

3.1 HTTP 在"裸奔":亲手抓包

sudo dnf install -y tcpdump
# 抓 80 端口流量:GET、Host、User-Agent 甚至标题全是明文
sudo tcpdump -i any -A -nn 'tcp port 80' | grep -a -E "GET |Host:|User-Agent|<title>"

3.2 HTTPS 与证书原理

3.3 开放 443 端口(80 不能关)

sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --list-services    # 应包含 ssh、http、https
80 必须保留的两个理由:① http:// 访问要靠它跳转到 HTTPS;② 证书每次续期仍走 80 验证,关掉 80 三个月后证书就无法续期。

3.4 申请证书:先演练再正式

sudo dnf install -y certbot
# 演练(走测试环境,不占用正式申请失败次数):
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

sudo certbot certificates    # 列出证书、到期时间、文件位置
sudo openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -noout -issuer -dates -ext subjectAltName

证书目录里关键的两个人文件:fullchain.pem(证书+中间证书,发给访问者)、privkey.pem(私钥,绝不外泄——和第 1 讲的 SSH 私钥同一个道理)。

3.5 改写 Nginx:80 跳转,443 提供内容

# 先备份;备份文件名不能以 .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 端口:验证文件 + 跳转
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;
    }
}
sudo nginx -t && sudo systemctl reload nginx
# Windows PowerShell 里要写 curl.exe(curl 是别名)
curl.exe -I http://example.com    # 应返回 301
curl.exe -I https://example.com   # 应返回 200

3.6 自动续期:定时器 + 钩子

sudo systemctl enable --now certbot-renew.timer   # 每天检查,到期前约 30 天自动续期
systemctl list-timers 'certbot*'

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   # 手动跑一次确认无误
sudo certbot renew --dry-run    # 演练续期,应显示 all simulated renewals succeeded

3.7 再抓包对比

sudo tcpdump -i any -A -nn -c 40 'tcp port 443'

这次看不到 GET、User-Agent 和网页标题,全是乱码——内容已被加密。但仍可能看到域名:建立加密连接前浏览器要先"报名字",这步是明文的。HTTPS 隐藏的是你看了什么内容,而不是你访问了哪个网站。

3.8 小结

步骤关键点
开门放行 443;80 不能关(跳转 + 续期都要用)
申请证书certbot webroot 方式;先 --dry-run 演练再正式
配置 HTTPS80 负责跳转,443 提供内容;privkey.pem 绝不外泄
自动续期certbot-renew.timer + deploy 钩子;靠自动化而不是靠人记