《服务器运维》课程学习记录 · Rocky Linux 9 · 阿里云 ECS
公网上的扫描程序昼夜不停地尝试登录 22 端口(暴力破解)。查看证据:
# 查看 SSH 登录失败记录(最后 30 条)
sudo journalctl -u sshd | grep -i "failed" | tail -30
tty 显示 /dev/tty1,SSH 登录显示 /dev/pts/0。私钥留在自己电脑上永不外传;公钥放到服务器上可以公开。先建普通用户:
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 会直接拒绝使用
绝不能把自己的私钥发给老师;正确做法是安装老师的公钥。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 可用"
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 生效(不断开当前连接)
安全组是云平台层的门,firewalld 是系统内的门,两层都要按最小化原则配置。ICMP 规则端口显示"任何"不是漏洞——ICMP 本身不使用端口,只是占位符,保留它方便 ping 诊断。
sudo firewall-cmd --permanent --add-service=ssh # 必须加 --permanent,否则重启失效
sudo firewall-cmd --reload
sudo firewall-cmd --list-all # 核对放行结果
sudo ss -tlnp # 查看本机实际监听的端口
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
| 攻击者想做的事 | 部署的加固 |
|---|---|
| 猜密码登进来 | SSH 密钥登录 + 禁用密码 + 禁止 root |
| 从别的端口钻进来 | firewalld + 安全组,两层最小化放行 |
| 反复爆破 SSH | fail2ban 自动封禁 |
| 进来了不被发现 | journal 日志审计 |
DNS 是互联网的"通讯录",把域名翻译成 IP。在阿里云云解析添加两条 A 记录:主机记录分别填 @(域名本身)和 www,记录值为服务器公网 IP,TTL 默认 10 分钟。
nslookup 你的域名 # Address 一栏应是服务器公网 IP
nslookup www.你的域名
两层门都要开(安全组添加 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
sudo dnf install -y nginx
sudo systemctl enable --now nginx # 立即启动 + 开机自启
systemctl status nginx # 应显示 active (running)
nginx -v
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
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
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 # 返回默认测试页
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
sudo tail -f /var/log/nginx/mysite_access.log # Ctrl+C 退出;日志是运维最重要的信息来源
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>"
http://域名/.well-known/acme-challenge/ 读到它才签发 → 三个前提:解析正确、80 端口开放、Nginx 能发出验证文件。sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --list-services # 应包含 ssh、http、https
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 私钥同一个道理)。
# 先备份;备份文件名不能以 .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
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
sudo tcpdump -i any -A -nn -c 40 'tcp port 443'
这次看不到 GET、User-Agent 和网页标题,全是乱码——内容已被加密。但仍可能看到域名:建立加密连接前浏览器要先"报名字",这步是明文的。HTTPS 隐藏的是你看了什么内容,而不是你访问了哪个网站。
| 步骤 | 关键点 |
|---|---|
| 开门 | 放行 443;80 不能关(跳转 + 续期都要用) |
| 申请证书 | certbot webroot 方式;先 --dry-run 演练再正式 |
| 配置 HTTPS | 80 负责跳转,443 提供内容;privkey.pem 绝不外泄 |
| 自动续期 | certbot-renew.timer + deploy 钩子;靠自动化而不是靠人记 |