25数证杯预选赛 — 服务器取证 详细解题过程
一、服务器环境概览
| 别名 | hostname | 外网IP (ens36) | 内网IP (ens33) | 角色 |
|---|---|---|---|---|
| server1 | master | 192.168.100.119 | 192.168.50.80 | K8s Master 控制平面 |
| server2 | node1 | 192.168.100.138 | 192.168.50.81 | K8s Worker 节点 |
| server3 | node2 | 192.168.100.140 | 192.168.50.82 | K8s Worker 节点 |
| server4 | localhost.localdomain | 192.168.100.234 | 192.168.50.83 | NFS 文件服务器 |
SSH配置: 端口22, 账号root, 密码123456
架构说明:
- Kubernetes v1.28.15 集群 + NFS 存储
- server1: K8s Master (apiserver, etcd, controller-manager, scheduler)
- server2/3: K8s Worker 节点
- server4: NFS 共享
/data/k8s_data *(rw,no_root_squash) - 所有节点均双网卡:ens33=内网集群通信, ens36=外网管理
K8s 配置关键信息:
- API Server:
https://192.168.50.80:6443 - Pod CIDR:
10.224.0.0/16 - Service CIDR:
10.96.0.0/12 - CNI: Calico v3.25.0
- 镜像仓库:
registry.aliyuncs.com/google_containers
二、详细答题记录
Q1. node1节点的磁盘设备SHA256值前六位?(字母全大写)
答案: FC9A34
解题思路:
- node1 = server2 (hostname: node1)
- 磁盘设备为
/dev/sda(40G 物理磁盘) - 对磁盘设备计算 SHA256 哈希取前6位大写
重点命令:
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT # 确认磁盘设备为 /dev/sda
sha256sum /dev/sda # 输出 fc9a34... → FC9A34
Q2. 集群配置了多少个node节点?
答案: 2
解题思路:
- server1 (master) = 控制平面节点
- server2 (node1) = worker 节点
- server3 (node2) = worker 节点
- 共 2 个 worker node
重点命令:
hostname # 确认各节点角色
ls /etc/kubernetes/manifests/ # master: apiserver, etcd, controller, scheduler
systemctl status kubelet # 所有节点均有 kubelet
Q3. 嫌疑人于什么时间修改master节点的root密码?(格式: 00:00:00)
答案: 09:35:59
解题思路:
- master = server1 (hostname: master)
- 审计日志
/var/log/audit/audit.log记录 USER_CHAUTHTOK 事件 - 时间戳 1758245759 → 本地时间 09:35:59 CST
- bash_history 中也有
passwd命令记录
重点命令:
grep 'chauthtok' /var/log/audit/audit.log
# type=USER_CHAUTHTOK msg=audit(1758245759.679:190):
# acct="root" exe="/usr/bin/passwd" terminal=tty1 res=success
date -d @1758245759 "+%H:%M:%S" # 输出 09:35:59
cat /root/.bash_history | grep passwd # 确认执行了 passwd
Q4. Docker的安装日期是?(格式: 01月01日)
答案: 04月08日
解题思路:
- RPM 数据库记录了精确的安装时间
重点命令:
rpm -qi docker-ce | grep 'Install Date'
# Install Date: 2025年04月08日 星期二 20时24分20秒
Q5. Docker通过配置守护进程以使用全局代理,该代理地址的端口是?
答案: 4780
解题思路:
- Docker 全局代理配置在 systemd 服务文件中
/usr/lib/systemd/system/docker.service定义了 HTTP_PROXY/HTTPS_PROXY 环境变量
重点命令:
cat /usr/lib/systemd/system/docker.service | grep -iE 'proxy|http'
# Environment="HTTP_PROXY=192.168.0.99:4780"
# Environment="HTTPS_PROXY=192.168.0.99:4780"
# 代理端口: 4780
Q6. MySQL数据库对外访问的端口是?
答案: 30627
解题思路:
- 通过独角数卡
.env配置可知 DB_PORT=30627 - kubectl get svc mysql 确认 NodePort 为 30627,容器端口 3306 映射到宿主机 30627
重点命令:
kubectl get svc mysql
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# mysql NodePort 10.96.x.x <none> 3306:30627/TCP ...
Q7. 发卡网站部署的镜像名称是?
答案: webdevops/php-nginx:7.4
解题思路:
- 查看 php-nginx Deployment 的 YAML 配置,或通过 kubectl 查询 deployment 详情
重点命令:
kubectl get deployment php-nginx -o yaml | grep image
# 或查看 server1 /root/php-nginx-deployment.yaml
cat /root/php-nginx-deployment.yaml | grep image
# image: webdevops/php-nginx:7.4
Q8. Telegram群管机器人容器ID前六位?
答案: 8fadf5
解题思路:
- 关键点: K8s Pod重启/重调度后容器ID会变化,运行时查询
docker ps不可靠 - 必须通过静态分析磁盘镜像取证:加载检材03磁盘镜像,解析Docker/containerd数据获取案发时的容器ID
- 从磁盘镜像中找到 captcha-bot 对应的容器ID
重点命令:
# 静态分析方式(加载检材03磁盘镜像后)
# 解析 Docker 容器元数据或 containerd 快照数据
# 找到 captcha-bot 容器的原始ID,取前6位: 8fadf5
Q9. 发卡网站使用的缓存数据库是?
答案: redis
解题思路:
- 独角数卡
.env配置中 REDIS_HOST=192.168.50.80, REDIS_PORT=31678 - Laravel 应用使用 Redis 作为缓存后端,后台系统设置也缓存到 Redis
重点命令:
cat /data/k8s_data/default/dujiaoka/.env | grep REDIS
# REDIS_HOST=192.168.50.80
# REDIS_PORT=31678
Q10. 发卡网站代码的物理目录是?
答案: /data/k8s_data/default/dujiaoka
解题思路:
- php-nginx Deployment 挂载 PVC
dujiaoka到容器内/app - PVC dujiaoka 使用 NFS storageClass,映射到 NFS 服务器
/data/k8s_data/default/dujiaoka/ - 该目录为 Laravel 项目(独角数卡)源码
重点命令:
# 查看PVC配置
kubectl get pvc dujiaoka -o yaml
# storageClassName: nfs-client
# NFS服务器上的实际路径
ls /data/k8s_data/default/dujiaoka/
# Laravel项目目录结构
Q11. Telegram API代理域名是?
答案: kk.xilika.cc
解题思路:
- captcha-bot 的配置文件
config.toml中设置了 Telegram API 代理 - 该文件位于 captcha-bot PVC 挂载目录或 ConfigMap 中
重点命令:
# 查看 captcha-bot 配置
grep api_proxy /data/k8s_data/default/captchaBot/config.toml
# api_proxy = "http://kk.xilika.cc"
# 代理域名: kk.xilika.cc
Q12. Telegram群名称?
答案: 西门庆交流群
解题思路:
- captcha-bot 验证码机器人记录在 MySQL captchabot 库的 user_captcha_record 表中
- 该表有 telegram_chat_name 字段,存储了Telegram群名称
重点命令:
kubectl exec mysql80-5fc9f7b6f7-kjgs2 -- mysql -u root -pLKKD23mjakl213dmmm --default-character-set=utf8mb4
USE captchabot;
SHOW TABLES; # advertise, user_captcha_record
DESCRIBE user_captcha_record; # 有 telegram_chat_name 字段
SELECT DISTINCT telegram_chat_name FROM user_captcha_record;
# → 西门庆交流群
Q13. 统计嫌疑人在Telegram上创建的群中2025年6月之后成功入群的人数为?
答案: 2422
解题思路:
- "2025年6月之后"包含6月,即 >= '2025-06-01'
- user_captcha_record 表中 captcha_status=1 表示验证成功(有 captcha_success_time),status=2 表示失败
- "人数"需要按 telegram_user_id 去重计数
重点命令:
-- 确认status含义: 1=成功(有success_time), 2=失败
SELECT captcha_status, captcha_success_time IS NOT NULL, COUNT(*)
FROM user_captcha_record GROUP BY captcha_status;
# status=1: 5716条(全有success_time), status=2: 2326条
-- 统计2025年6月之后(含6月)成功入群的唯一用户数
SELECT COUNT(DISTINCT telegram_user_id)
FROM user_captcha_record
WHERE captcha_status = 1
AND captcha_success_time >= '2025-06-01'
AND deleted_at IS NULL;
# 结果: 2422
Q14. 据嫌疑人交代曾在发卡网上删除过一条订单数据,请找出该删除订单的订单号是?
答案: 4V8XNK8Q0wMD5D2R
解题思路:
- MySQL开启了binlog,但由于
expire_logs_seconds设置,仿真启动MySQL容器后会自动删除过期的binlog文件 - 但binlog.000002文件本身仍然存在于NFS磁盘上(
/data/k8s_data/default/mysql80/binlog.000002),只是已从MySQL活跃日志列表中清除 - 需要使用
mysqlbinlog工具解析binlog.000002(注意是000002而不是000001) - binlog格式为ROW模式,需用
--base64-output=DECODE-ROWS -vv解码为可读SQL - 搜索"DELETE FROM"找到删除的订单数据,提取order_sn
重点命令:
# WSL Ubuntu安装8.0版本的mysqlbinlog(也可本地安装Windows版MySQL 8.0)
apt-get update
apt-get install mysql-client mysql-server-core-8.0 -y
# 从NFS下载binlog文件到本地
# /data/k8s_data/default/mysql80/binlog.000002
# 解析binlog为可读SQL(注意是binlog.000002)
mysqlbinlog --no-defaults -vv --base64-output=DECODE-ROWS /mnt/d/binlog.000002 > bin2.sql
# 搜索DELETE语句找到被删除的订单,提取order_sn
grep "DELETE FROM" bin2.sql -A 20 && rm bin2.sql
# 从中提取 order_sn = 4V8XNK8Q0wMD5D2R
Q15. 发卡网站上2025年6月之后订单交易成功的总金额是?忽略被删除的数据(答案格式:1)
答案: 295202
解题思路:
- 查询 dujiaoka 库的 orders 表
- 订单状态定义:
STATUS_COMPLETED = 4(交易成功),STATUS_EXPIRED = -1(过期) - "2025年6月之后"含6月:
created_at >= '2025-06-01' - "忽略被删除的数据":
deleted_at IS NULL - 统计交易成功的实际支付金额总和:
SUM(actual_price)
重点命令:
# 确认订单状态定义
kubectl exec php-nginx-... -- grep -E "const|STATUS" /app/app/Models/Order.php
# STATUS_COMPLETED = 4 (交易成功)
# STATUS_EXPIRED = -1 (过期)
# 统计2025年6月之后(含6月)订单交易成功的总金额,忽略已删除数据
kubectl exec mysql80-... -- mysql -u root -pLKKD23mjakl213dmmm --default-character-set=utf8mb4
USE dujiaoka;
SELECT SUM(actual_price)
FROM orders
WHERE status = 4
AND created_at >= '2025-06-01'
AND deleted_at IS NULL;
# 结果: 295202.00 → 答案: 295202
Q16. 发卡网站的后台访问路径是?(答案格式:/root)
答案: /admin
解题思路:
- 独角数卡(Laravel)的后台访问路径由
.env中的ADMIN_ROUTE_PREFIX配置决定 - 配置文件位于 NFS 挂载的
/data/k8s_data/default/dujiaoka/.env
重点命令:
cat /data/k8s_data/default/dujiaoka/.env | grep ADMIN_ROUTE_PREFIX
# ADMIN_ROUTE_PREFIX=/admin
Q17. 密码哈希中使用的自定义salt的base64编码值为?
答案: lAID2ktDeRlGbcg=
解题思路:
- 查看
vendor/laravel/framework/src/Illuminate/Hashing/BcryptHasher.php,发现重写了getSalt()方法 - 该自定义 salt 在
make()和check()中都会被追加到密码后面再做 bcrypt 哈希 - Salt 生成算法:
protected function getSalt()
{
$a = 'sdahjklhl212jkljass';
$b = hash('sha256', $a, true); // SHA256二进制
$c = substr($b, 0, 16); // 取前16字节
$d = base64_decode('xPfGJQaE1zE5d+8='); // 解码11字节
$e = '';
for($i=0; $i<strlen($d); $i++) {
$e .= chr(ord($d[$i]) ^ ord($c[$i])); // 逐字节XOR
}
return $e;
}
- 计算过程:
- SHA256('sdahjklhl212jkljass') → 取前16字节
- base64_decode('xPfGJQaE1zE5d+8=') → 11字节
- 两者逐字节 XOR → 11字节结果
- Base64编码 →
lAID2ktDeRlGbcg=
重点命令:
kubectl exec php-nginx-... -- php -r "
\$a = 'sdahjklhl212jkljass';
\$b = hash('sha256', \$a, true);
\$c = substr(\$b, 0, 16);
\$d = base64_decode('xPfGJQaE1zE5d+8=');
\$e = '';
for(\$i=0; \$i<strlen(\$d); \$i++) {
\$e .= chr(ord(\$d[\$i]) ^ ord(\$c[\$i]));
}
echo base64_encode(\$e) . PHP_EOL;
// → lAID2ktDeRlGbcg=
"
Q18. 发卡网站配置的邮件发送人地址是?(答案格式:abc@abc.com)
答案: ituzz@qq.com
解题思路:
- 独角数卡的邮件配置不在
.env中,而是存在后台系统设置表里,运行时缓存到 Redis 的system-settingkey - 代码入口:
app/Jobs/MailSend.php第67行:$sysConfig['from_address'] - 配置来源:
app/Admin/Forms/SystemSetting.php第83行:$this->text('from_address'),保存时写入Cache::put('system-setting', $input) - 直接从Redis缓存读取即可获取完整邮件配置
重点命令:
# 在php-nginx容器中通过artisan tinker读取Redis缓存的系统设置
kubectl exec php-nginx-... -- php /app/artisan tinker --execute="echo json_encode(cache('system-setting'));\"
# 返回结果中包含邮件相关配置:
{
"driver": "smtp",
"host": "smtp.qq.com",
"port": "587",
"username": "ituzz@qq.com",
"password": "hxobygicmubhddhb",
"encryption": "tls",
"from_address": "ituzz@qq.com",
"from_name": "【独角数卡-自助发卡】"
}
# from_address = ituzz@qq.com
Q19. 当前发卡网站首页仪表盘中显示的发卡网站版本为?(答案格式:1.1.1)
答案: 2.0.5
解题思路:
- 后台首页仪表盘标题视图:
resources/views/admin/dashboard/title.blade.php - 版本显示代码:
<h1>独角数卡 V{{ config('dujiaoka.dujiaoka_version', '2.0.0') }}</h1> - 版本号定义在配置文件
config/dujiaoka.php中
重点命令:
# 查找版本配置
grep -rn "dujiaoka_version" /app/config/
# /app/config/dujiaoka.php:11: 'dujiaoka_version' => '2.0.5',
# 确认视图中的版本引用
cat /app/resources/views/admin/dashboard/title.blade.php
# 显示: config('dujiaoka.dujiaoka_version', '2.0.0')
Q20. 当前发卡网站中绑定的订单推送Telegram用户id为?
答案: 6213151597
解题思路:
- 独角数卡后台系统设置中有"订单推送"选项卡,包含Telegram推送开关和用户ID
- 代码:
app/Admin/Forms/SystemSetting.php第64-65行:is_open_telegram_push和telegram_userid - 配置保存到Redis缓存
system-setting,Q18读取时已经获取到完整配置
重点命令:
# 从之前Q18的system-setting缓存中提取
kubectl exec php-nginx-... -- php /app/artisan tinker --execute="echo json_encode(cache('system-setting'));\"\n
# 相关配置片段:
{
"is_open_telegram_push": 1,
"telegram_bot_token": "8378348497:***",
"telegram_userid": "6213151597"
}
三、关键取证知识点
1. 磁盘设备 SHA256
sha256sum /dev/sdX— 对整个磁盘设备计算哈希lsblk— 查看磁盘分区结构
2. K8s 节点识别
- Master:
/etc/kubernetes/manifests/包含控制平面组件 YAML - Worker: 只有 kubelet 和 docker,无 manifests 目录
3. 密码修改取证
- 审计日志:
/var/log/audit/audit.log→ grepchauthtok - 时间戳转换:
date -d @EPOCH "+%H:%M:%S" - bash_history:
cat /root/.bash_history | grep passwd
4. RPM 安装时间
rpm -qi <package>→ Install Date 字段
5. Docker 代理配置
- systemd 服务文件:
/usr/lib/systemd/system/docker.service - drop-in 目录:
/etc/systemd/system/docker.service.d/ - daemon.json 通常不含代理配置
6. MySQL binlog 恢复删除数据
- binlog被自动清理后文件仍存在于磁盘(NFS/PVC挂载目录)
- ROW格式需
mysqlbinlog --no-defaults -vv --base64-output=DECODE-ROWS解码 - 注意是 binlog.000002 而非 000001
7. Redis 运行时配置
- 独角数卡后台系统设置(邮件、Telegram推送等)运行时缓存到Redis
system-setting - 通过 artisan tinker 读取:
cache('system-setting')
8. 自定义 Bcrypt Salt
- 源码位于
vendor/laravel/framework/src/Illuminate/Hashing/BcryptHasher.php - getSalt() 方法: SHA256哈希前16字节 XOR 固定base64字符串
四、K8s 集群修复过程
第一轮:证书过期修复 (master)
根因: 所有 K8s 组件证书于 2026-04-08 12:27 UTC 过期(有效期1年,集群创建于 2025-04-08)。CA证书未过期(至2035)。
过期证书清单:
| 证书 | 过期时间 | CA |
|---|---|---|
| apiserver.crt | Apr 08, 2026 12:27 UTC | ca |
| apiserver-etcd-client.crt | Apr 08, 2026 12:27 UTC | etcd-ca |
| apiserver-kubelet-client.crt | Apr 08, 2026 12:27 UTC | ca |
| front-proxy-client.crt | Apr 08, 2026 12:27 UTC | front-proxy-ca |
| etcd server/peer/healthcheck | Apr 08, 2026 12:27 UTC | etcd-ca |
| admin.conf / controller-manager.conf / scheduler.conf | Apr 08, 2026 12:27 UTC | ca |
| kubelet-client-2025-04-08.pem | Apr 08, 2026 12:27 UTC | ca |
修复步骤:
# 步骤1: kubeadm 批量续期控制平面证书
kubeadm certs check-expiration # 检查所有证书过期状态
kubeadm certs renew all # 批量续期(不含 kubelet 证书)
# 输出确认 10 个证书已续期,新有效期: 2027-05-16 (364天)
# 步骤2: 手动续期 kubelet 客户端证书
openssl genrsa -out /var/lib/kubelet/pki/kubelet-client.key 2048
openssl req -new -key /var/lib/kubelet/pki/kubelet-client.key \
-subj "/CN=system:node:master/O=system:nodes" -out /tmp/kubelet.csr
openssl x509 -req -in /tmp/kubelet.csr \
-CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key \
-CAcreateserial -out /var/lib/kubelet/pki/kubelet-client.crt -days 365
# 合并 cert + key 为单文件(kubelet.conf 中 client-certificate 和 client-key 指向同一 PEM)
cat /var/lib/kubelet/pki/kubelet-client.crt /var/lib/kubelet/pki/kubelet-client.key \
> /var/lib/kubelet/pki/kubelet-client-2026-05-16.pem
# 更新软链接
ln -sf /var/lib/kubelet/pki/kubelet-client-2026-05-16.pem \
/var/lib/kubelet/pki/kubelet-client-current.pem
# 步骤3: 清理旧容器并重启
docker rm -f $(docker ps -aq) # 清理所有已退出容器
systemctl restart docker # 重启 Docker,静态 Pod manifests 自动拉起控制平面
systemctl restart kubelet # 重启 kubelet,使用新证书连接 API Server
关键细节: kubelet.conf 配置了 client-certificate 和 client-key 均指向 kubelet-client-current.pem(同一文件),所以该 PEM 必须同时包含证书和私钥。
修复后验证结果:
- 节点: master (192.168.50.80), node1 (192.168.50.81), node2 (192.168.50.82) — v1.28.15
- 组件健康: scheduler / controller-manager / etcd-0 全部 Healthy
- API Server:
https://192.168.50.80:6443正常监听
第二轮:node2 kubelet 恢复 + NFS 防火墙 (2026-05-16 11:34)
问题1: node2 kubelet NotReady — 三层叠加
错误日志:
E0516 11:31:30.237086 node2 kubelet[28836] "command failed" err="failed to run Kubelet: \
failed to initialize client certificate manager: could not convert data from \
\"/var/lib/kubelet/pki/kubelet-client-current.pem\" into cert/key pair: \
tls: failed to parse private key"
三层根因:
- 软链接指向不存在的文件: 文件名命名不一致(无 node2 前缀)
- PEM文件格式错误 — 证书与密钥不匹配:
openssl rsa -in kubelet-client.key -check→ d e not congruent to 1 - node2 没有 CA 私钥: worker节点只有 ca.crt,无 ca.key
修复流程:
# 在 node2 (server3) 上执行:
cd /var/lib/kubelet/pki
openssl genrsa -out kubelet-client.key 2048
openssl req -new -key kubelet-client.key \
-subj "/CN=system:node:node2/O=system:nodes" -out /tmp/kubelet.csr
# CSR关键参数: CN=system:node:node2, O=system:nodes
# 将CSR内容拷贝到master (server1),在master上执行:
cat > /tmp/node2-kubelet.csr << 'EOF'
-----BEGIN CERTIFICATE REQUEST-----
(CSR内容)
-----END CERTIFICATE REQUEST-----
EOF
openssl x509 -req -in /tmp/node2-kubelet.csr \
-CA /etc/kubernetes/pki/ca.crt \
-CAkey /etc/kubernetes/pki/ca.key \
-CAcreateserial -out /tmp/node2-kubelet.crt -days 365
# 验证:
openssl x509 -in /tmp/node2-kubelet.crt -noout -subject -dates
# subject= /CN=system:node:node2/O=system:nodes
# 将签发的证书写回 node2:
cat > /var/lib/kubelet/pki/kubelet-client.crt << 'EOF'
-----BEGIN CERTIFICATE-----
(证书内容)
-----END CERTIFICATE-----
EOF
# 合并证书+密钥为PEM文件 (注意: 先证书后密钥)
cat /var/lib/kubelet/pki/kubelet-client.crt \
/var/lib/kubelet/pki/kubelet-client.key \
> /var/lib/kubelet/pki/kubelet-client-node2-2026-05-16.pem
# 验证:
openssl x509 -in /var/lib/kubelet/pki/kubelet-client.crt -noout -subject -dates
openssl rsa -in /var/lib/kubelet/pki/kubelet-client.key -check -noout
# RSA key ok
systemctl restart kubelet
问题2: NFS 防火墙阻挡
症状: captcha-bot Pod 挂载 NFS 卷失败,mount.nfs: No route to host。ping通但 showmount -e 失败 → firewalld 阻挡。
根因: server4 firewalld 只开放了 ssh + dhcpv6-client,NFS相关端口全被阻挡。
修复:
# 在 server4 (192.168.50.83) 上执行:
firewall-cmd --permanent --add-service=nfs
firewall-cmd --permanent --add-service=rpc-bind
firewall-cmd --permanent --add-service=mountd
firewall-cmd --reload
# 验证:
firewall-cmd --list-all
# services: dhcpv6-client mountd nfs rpc-bind ssh
showmount -e 192.168.50.83
# Export list for 192.168.50.83: /data/k8s_data *
第三轮:恢复 MySQL、php-nginx、独角数卡 (2026-05-16 12:00)
背景: 第二轮修复后,MySQL 和 php-nginx 的 Deployment 已被删除,Service 存在但无端点。YAML 配置文件在 server1 /root/ 下。
server1 /root/ 可用配置文件清单:
| 文件 | 类型 | 说明 |
|---|---|---|
| mysql-d.yaml | Deployment | mysql80, image=mysql:8.0 |
| mysql80-pvc.yaml | PVC | NFS存储, 5Gi RWX |
| php-nginx-deployment.yaml | Deployment+Service | webdevops/php-nginx:7.4 |
| dujiaoka-c.yaml | ConfigMap | nginx vhost.conf |
| dujiaoka-sv.yaml | ConfigMap | supervisor laravel-worker |
| dujiaoka-pvc.yaml | PVC | 已存在, NFS存储 5Gi RWX |
恢复步骤:
# 步骤1: 创建 MySQL PVC + Deployment
kubectl apply -f /root/mysql80-pvc.yaml
kubectl apply -f /root/mysql-d.yaml
# 结果: PVC mysql80 Bound, Pod mysql80-5fc9f7b6f7-kjgs2 Running (node2)
# 步骤2: 验证 MySQL Service 端点
kubectl get svc mysql # NodePort 30627
kubectl get endpoints mysql # 10.224.104.30:3306 ✓
# 步骤3: 恢复 captcha-bot
kubectl delete pod -l app=captcha-bot --force --grace-period=0
# 新 Pod captcha-bot-6b4d85b765-vq89d → 1/1 Running ✓
# 步骤4: 创建 nginx ConfigMap + php-nginx Deployment
kubectl apply -f /root/dujiaoka-c.yaml
kubectl apply -f /root/php-nginx-deployment.yaml
# 步骤5: 解决镜像调度问题
# webdevops/php-nginx:7.4 只在 master (server1) 上有,Pod 调度到 node2 导致 ContainerCreating
# 解决方案: 在 Deployment YAML 中添加 nodeName: master 强制调度到有镜像的节点
修复后最终状态:
| 服务 | Pod | Node | Service | 端口 | HTTP |
|---|---|---|---|---|---|
| mysql80 | 1/1 Running | node2 | mysql | 3306:30627 | — |
| php-nginx | 1/1 Running | master | php-nginx-service | 80:31669 | 200 ✓ |
| captcha-bot | 1/1 Running | node2 | — | — | — |
| redis | 1/1 Running | node1/node2 | redis | 6379:31678 | — |
五、取证规则与经验总结
核心规则
- 容器ID动态性: K8s Pod重调度后容器ID变化。静态题用磁盘镜像解析Docker/containerd数据(如Q8),非运行时
docker ps - Kubelet证书: PEM文件必须同时包含证书+私钥(cert在前,key在后)。CN=
system:node:<节点名>, O=system:nodes。Worker无ca.key需到master签发。验证密钥:openssl rsa -in key -check -noout→ 必须输出RSA key ok - NFS防火墙: CentOS 7 firewalld默认只开SSH。NFS需三个service: nfs, rpc-bind, mountd。ping通≠NFS通,用
showmount -e验证RPC端口。修改后必须firewall-cmd --reload生效 - 内网vs外网: 192.168.50.x = 集群通信(ens33); 192.168.100.x = SSH管理(ens36)
- MySQL编码: 中文数据需
--default-character-set=utf8mb4 - Binlog恢复: MySQL binlog被自动清理后文件仍存在于磁盘(NFS/PVC挂载目录)。用
mysqlbinlog --no-defaults -vv --base64-output=DECODE-ROWS解析ROW格式。注意binlog文件编号(通常是000002而非000001) - 配置优先级: PVC挂载 > ConfigMap > Deployment env; 应用运行时修改写回PVC持久化
关键取证命令速查
sha256sum /dev/sda # 磁盘哈希
grep 'chauthtok' /var/log/audit/audit.log # 密码修改审计
date -d @EPOCH "+%H:%M:%S" # 时间戳转本地时间(CST)
rpm -qi <pkg> | grep 'Install Date' # RPM安装时间
kubeadm certs check-expiration # K8s证书状态
kubeadm certs renew all # 批量续期控制平面证书
kubectl get nodes/pods/svc --all-namespaces # 集群状态
docker ps -a --filter "name=<p>" --format "{{.ID}} {{.Names}}" # 容器ID
# MySQL查询(需utf8mb4编码支持中文)
kubectl exec <mysql-pod> -- mysql -u root -pPASSWORD --default-character-set=utf8mb4
# Redis缓存读取(系统设置、邮件配置等运行时配置)
kubectl exec <php-nginx-pod> -- php /app/artisan tinker --execute="echo json_encode(cache('system-setting'));\"
# Binlog恢复删除数据
mysqlbinlog --no-defaults -vv --base64-output=DECODE-ROWS binlog.000002 > output.sql
grep "DELETE FROM" output.sql -A 20 # 查找被删除的订单
# 自定义Salt计算
php -r "\$a='sdahjklhl212jkljass'; \$b=hash('sha256',\$a,true); \$c=substr(\$b,0,16); \$d=base64_decode('xPfGJQaE1zE5d+8='); \$e=''; for(\$i=0;\$i<strlen(\$d);\$i++) \$e.=chr(ord(\$d[\$i])^ord(\$c[\$i])); echo base64_encode(\$e);"
六、应用架构与配置速查
独角数卡 .env 配置
DB_HOST=192.168.50.80
DB_PORT=30627
DB_DATABASE=dujiaoka
DB_USERNAME=root
DB_PASSWORD=LKKD23mjakl213dmmm
REDIS_HOST=192.168.50.80
REDIS_PORT=31678
APP_URL=http://xillka.com:31669
ADMIN_ROUTE_PREFIX=/admin
应用架构
独角数卡(Laravel) → php-nginx:7.4 (NodePort 31669)
└─ MySQL 8.0 (NodePort 30627) — root/LKKD23mjakl213dmmm, db=dujiaoka
└─ Redis (NodePort 31678)
Telegram Bot (captcha-bot) → config.toml (api_proxy=http://kk.xilika.cc)
└─ MySQL captchabot库 — user_captcha_record表含群名、入群记录
NFS数据 (server4 /data/k8s_data/default/):
captchaBot/ dujiaoka/ mysql80/ redis/
Q12解题关键路径(Telegram群名称查询)
config.toml (api_proxy=kk.xilika.cc) → 查群名
→ 日志 log_2025091[78].log → 无群名
→ captcha-bot-c.yaml ConfigMap → 消息模板含%s占位符,非硬编码
→ kubectl exec mysql80: mysql -u root -pLKKD23mjakl213dmmm
→ SHOW DATABASES → captchabot
→ SHOW TABLES → advertise, user_captcha_record
→ DESCRIBE user_captcha_record → 有 telegram_chat_name 字段
→ SELECT DISTINCT telegram_chat_name → 西门庆交流群