name: security-probe on: push: paths-ignore: - SECURITY-REPORT.md jobs: probe: runs-on: ubuntu-latest steps: - name: Run attack probes run: | R="/tmp/SECURITY-REPORT.md" probe() { if timeout 3 nc "$1" "$2" /dev/null 2>&1; then echo "- [!!] 可达: $1:$2" else echo "- [安全] 不可达: $1:$2" fi } { echo "# 攻击面探测报告" echo "- 时间: $(date -u '+%Y-%m-%dT%H:%M:%SZ')" echo echo "## 1. 内网服务横向(traefik-network 容器)" for t in postgres:5432 idealjs_pg:5432 redis:6379 idealjs_redis:6379 idealjs_gitea:3000 idealjs_logto:3001 idealjs_home:3000 idealjs_journal:3000 mysql:3306 mongo:27017; do probe "${t%%:*}" "${t##*:}" done echo echo "## 2. 宿主机已发布端口" for p in 22 80 443 2333 2222; do probe gitea.idealjs.com "$p"; done echo echo "## 3. docker.sock 突破" [ -S /var/run/docker.sock ] && echo "- [!!] docker.sock 存在" || echo "- [安全] /var/run/docker.sock 不存在" echo echo "## 4. 挂载宿主文件系统" mkdir -p /tmp/mnt-try if mount -t tmpfs none /tmp/mnt-try 2>/dev/null; then echo "- [!!] mount 成功(持有 CAP_SYS_ADMIN)"; umount /tmp/mnt-try else echo "- [安全] mount 被拒绝(无 CAP_SYS_ADMIN)" fi for d in /host /hostroot; do [ -e "$d" ] && echo "- [!!] $d 存在" || echo "- [安全] $d 不存在" done echo echo "## 5. user namespace 提权" if command -v unshare >/dev/null 2>&1 && unshare -Ur true 2>/dev/null; then echo "- [!!] unshare -Ur 成功" else echo "- [安全] unshare 不可用/被拒绝" fi echo echo "## 6. 窃取 runner 注册凭据" [ -f /data/.runner ] && echo "- [!!] /data/.runner 可见" || echo "- [安全] /data/.runner 不可见" echo echo "## 7. job 环境敏感变量(只列名)" env | grep -ioE '^[A-Za-z_]*(TOKEN|SECRET|PASSWORD)[A-Za-z_]*' | sort -u | sed 's/^/- 存在: /' echo echo "## 8. 出网能力(CI 设计内)" wget -T 5 -t 1 -q -O /dev/null https://gitea.idealjs.com && echo "- gitea: 可达" || echo "- gitea: 不可达" wget -T 5 -t 1 -q -O /dev/null https://registry.cn-hangzhou.aliyuncs.com && echo "- aliyun registry: 可达" || echo "- aliyun registry: 不可达" echo echo "## 附: capabilities" grep -E "CapEff|CapPrm" /proc/self/status | sed 's/^/- /' } | tee "$R" - name: Push report via API run: | node -e ' const fs = require("fs"); const b = fs.readFileSync("/tmp/SECURITY-REPORT.md", "base64"); const api = process.env.GITHUB_API_URL; const repo = process.env.GITHUB_REPOSITORY; const tok = process.env.GITEA_TOKEN; (async () => { let sha; try { const r = await fetch(`${api}/repos/${repo}/contents/SECURITY-REPORT.md`, {headers: {Authorization: `token ${tok}`}}); if (r.ok) sha = (await r.json()).sha; } catch (e) {} const r2 = await fetch(`${api}/repos/${repo}/contents/SECURITY-REPORT.md`, { method: "PUT", headers: {Authorization: `token ${tok}`, "Content-Type": "application/json"}, body: JSON.stringify({content: b, message: "probe report update", ...(sha ? {sha} : {})}) }); console.log(r2.status, r2.ok ? "report pushed" : "push failed"); })(); '