create-dwy 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/index.js +1 -1
- package/package.json +2 -2
- package/src/create.js +61 -37
- package/src/sync.js +212 -61
- package/templates/claude-global/CLAUDE.md +92 -0
- package/templates/claude-global/rules/dwy-android-core.md +610 -0
- package/templates/claude-global/rules/dwy-api-design.md +110 -0
- package/templates/claude-global/rules/dwy-db-migration.md +46 -0
- package/templates/claude-global/rules/dwy-flutter-core.md +564 -0
- package/templates/claude-global/rules/dwy-flutter-network.md +222 -0
- package/templates/claude-global/rules/dwy-flutter-router.md +97 -0
- package/templates/claude-global/rules/dwy-flutter-testing.md +94 -0
- package/templates/claude-global/rules/dwy-git-commit-convention.md +82 -0
- package/templates/claude-global/rules/dwy-git-security.md +53 -0
- package/templates/claude-global/rules/dwy-payload-limits.md +178 -0
- package/templates/claude-global/rules/{prefer-open-source.md → dwy-prefer-open-source.md} +4 -0
- package/templates/claude-global/rules/dwy-project-structure.md +181 -0
- package/templates/claude-global/rules/{python-code-style.md → dwy-python-core.md} +292 -358
- package/templates/claude-global/rules/dwy-python-fastapi.md +272 -0
- package/templates/claude-global/rules/dwy-python-orm.md +61 -0
- package/templates/claude-global/rules/{backend-security.md → dwy-python-security.md} +99 -72
- package/templates/claude-global/rules/dwy-python-testing.md +53 -0
- package/templates/claude-global/rules/{server-security.md → dwy-server-security.md} +1 -2
- package/templates/claude-global/rules/dwy-tdd-development.md +70 -0
- package/templates/claude-global/rules/dwy-tech-stack.md +60 -0
- package/templates/claude-global/rules/dwy-vue-api.md +146 -0
- package/templates/claude-global/rules/{vue-code-style.md → dwy-vue-core.md} +338 -152
- package/templates/claude-global/rules/dwy-vue-pinia.md +75 -0
- package/templates/claude-global/rules/dwy-vue-testing.md +102 -0
- package/templates/claude-global/skills/dwy-deploy-audit/SKILL.md +481 -0
- package/templates/claude-global/skills/dwy-deploy-audit/references/checks-data.md +117 -0
- package/templates/claude-global/skills/dwy-deploy-audit/references/checks-network.md +74 -0
- package/templates/claude-global/skills/dwy-deploy-audit/references/checks-runtime.md +184 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_base.sh +39 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_capacity.sh +278 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_db.sh +122 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_docker.sh +249 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_env.sh +64 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_https.sh +60 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_logs.sh +209 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_nginx.sh +92 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_resilience.sh +157 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_secrets.sh +327 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_services.sh +77 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_ssh.sh +56 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/run_all.sh +105 -0
- package/templates/claude-global/skills/dwy-docker/SKILL.md +187 -0
- package/templates/claude-global/skills/dwy-docker/references/ask-templates.md +142 -0
- package/templates/claude-global/skills/dwy-docker/references/templates.md +479 -0
- package/templates/claude-global/skills/dwy-docker/references/version-rules.md +76 -0
- package/templates/claude-global/skills/dwy-docker/scripts/query_dockerhub.py +204 -0
- package/templates/claude-global/skills/dwy-docker/scripts/scan_images.sh +185 -0
- package/templates/claude-global/skills/dwy-eapi/SKILL.md +433 -0
- package/templates/claude-global/skills/dwy-eapi/references/tasks-integration-guide.md +387 -0
- package/templates/claude-global/skills/dwy-ekit/SKILL.md +85 -0
- package/templates/claude-global/skills/dwy-ekit/references/data.md +135 -0
- package/templates/claude-global/skills/dwy-ekit/references/form.md +68 -0
- package/templates/claude-global/skills/dwy-ekit/references/request.md +183 -0
- package/templates/claude-global/skills/dwy-ekit/references/ui.md +109 -0
- package/templates/claude-global/skills/{dwy-frontend-eui → dwy-eui}/SKILL.md +129 -15
- package/templates/claude-global/skills/dwy-eui/references/eui-design-guide.md +257 -0
- package/templates/claude-global/skills/dwy-eui/references/eui-integration-guide.md +302 -0
- package/templates/claude-global/skills/dwy-eui/references/eui-landing-design-guide.md +437 -0
- package/templates/claude-global/skills/dwy-fullstack-scaffold/SKILL.md +214 -0
- package/templates/claude-global/skills/dwy-mirror-source/SKILL.md +275 -0
- package/templates/claude-global/skills/dwy-mirror-source/references/dockerfile-snippets.md +228 -0
- package/templates/claude-global/skills/dwy-mirror-source/references/mirror-providers.md +150 -0
- package/templates/claude-global/skills/dwy-mirror-source/scripts/__pycache__/check_mirrors.cpython-313.pyc +0 -0
- package/templates/claude-global/skills/dwy-mirror-source/scripts/apply_mirrors.py +666 -0
- package/templates/claude-global/skills/dwy-mirror-source/scripts/check_mirrors.py +868 -0
- package/templates/claude-global/skills/dwy-pentest/SKILL.md +344 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/__init__.py +1 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/__init__.cpython-311.pyc +0 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/config.cpython-311.pyc +0 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/executor.cpython-311.pyc +0 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/severity.cpython-311.pyc +0 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/validator.cpython-311.pyc +0 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/config.py +109 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/executor.py +157 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/severity.py +96 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/validator.py +113 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/pentest_auth.py +289 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/pentest_recon.py +217 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/pentest_report.py +343 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/pentest_scan.py +205 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/pentest_ssl.py +208 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/pentest_vuln.py +205 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/pentest_web.py +330 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/pyproject.toml +14 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/setup_tools.sh +97 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/uv.lock +415 -0
- package/templates/claude-global/skills/{dwy-release → dwy-publish}/SKILL.md +42 -18
- package/templates/claude-global/skills/dwy-vue-core/SKILL.md +890 -0
- package/templates/project/backend/.dwy.hbs +5 -0
- package/templates/project/backend/.python-version +1 -0
- package/templates/project/backend/CLAUDE.md.hbs +40 -0
- package/templates/project/{web/backend → backend}/alembic/script.py.mako +2 -0
- package/templates/project/{web/backend → backend}/alembic.ini.hbs +0 -1
- package/templates/project/backend/deploy.sh.hbs +18 -0
- package/templates/project/{web → backend}/docker-compose.prod.yml.hbs +1 -1
- package/templates/project/{web/backend → backend}/pyproject.toml.hbs +4 -5
- package/templates/project/{web/backend → backend}/src/app/config.py.hbs +1 -1
- package/templates/project/backend/src/app/database.py +10 -0
- package/templates/project/backend/src/app/dependencies.py +5 -0
- package/templates/project/{web/backend → backend}/src/app/main.py.hbs +3 -3
- package/templates/project/backend/src/app/models/base.py +5 -0
- package/templates/project/web/CLAUDE.md.hbs +29 -36
- package/templates/project/web/{frontend → apps/web}/index.html.hbs +1 -0
- package/templates/project/web/{frontend → apps/web}/package.json.hbs +4 -3
- package/templates/project/web/apps/web/src/App.vue +9 -0
- package/templates/project/web/apps/web/src/api/client.ts +5 -0
- package/templates/project/web/apps/web/src/composables/.gitkeep +0 -0
- package/templates/project/web/apps/web/src/layouts/DefaultLayout.vue +8 -0
- package/templates/project/web/{frontend → apps/web}/src/router/index.ts +1 -1
- package/templates/project/web/{frontend → apps/web}/src/stores/auth.ts +4 -2
- package/templates/project/web/apps/web/src/style.css +2 -0
- package/templates/project/web/apps/web/src/types/router.d.ts +8 -0
- package/templates/project/web/apps/web/src/views/HomeView.vue +8 -0
- package/templates/project/web/apps/web/tsconfig.app.json +18 -0
- package/templates/project/web/apps/web/tsconfig.json +6 -0
- package/templates/project/web/{frontend → apps/web}/vite.config.ts.hbs +7 -1
- package/templates/project/web/package.json.hbs +16 -0
- package/templates/project/web/packages/.gitkeep +0 -0
- package/templates/project/web/pnpm-workspace.yaml +3 -0
- package/templates/claude-global/hooks/post-sync-claude-global.sh +0 -72
- package/templates/claude-global/rules/agent-team-execution.md +0 -138
- package/templates/claude-global/rules/agent-team-patterns.md +0 -157
- package/templates/claude-global/rules/git-commit-convention.md +0 -106
- package/templates/claude-global/rules/git-security.md +0 -144
- package/templates/claude-global/skills/dwy-backend-base/SKILL.md +0 -279
- package/templates/claude-global/skills/dwy-frontend-core/SKILL.md +0 -181
- package/templates/project/web/.claude/commands/deploy.md +0 -4
- package/templates/project/web/backend/src/app/models/base.py +0 -5
- package/templates/project/web/deploy.sh.hbs +0 -113
- package/templates/project/web/frontend/src/App.vue +0 -3
- package/templates/project/web/frontend/src/api/client.ts +0 -13
- package/templates/project/web/frontend/src/style.css +0 -1
- package/templates/project/web/frontend/src/views/HomeView.vue +0 -5
- package/templates/project/web/frontend/tsconfig.app.json +0 -14
- package/templates/project/web/frontend/tsconfig.json +0 -4
- /package/templates/{project/web/backend → claude-global/skills/dwy-pentest/scripts}/.python-version +0 -0
- /package/templates/project/{web/backend → backend}/Dockerfile +0 -0
- /package/templates/project/{web/backend → backend}/alembic/env.py.hbs +0 -0
- /package/templates/{claude-global/skills → project/backend/alembic/versions}/.gitkeep +0 -0
- /package/templates/project/{web → backend}/docker-compose.dev.yml.hbs +0 -0
- /package/templates/project/{web/backend → backend}/src/app/__init__.py +0 -0
- /package/templates/project/{web/backend → backend}/src/app/models/__init__.py +0 -0
- /package/templates/project/{web/backend → backend}/src/app/routers/__init__.py +0 -0
- /package/templates/project/{web/backend → backend}/src/app/routers/health.py +0 -0
- /package/templates/project/{web/backend → backend}/src/app/schemas/__init__.py +0 -0
- /package/templates/project/{web/backend → backend}/src/app/services/__init__.py +0 -0
- /package/templates/project/{web/backend → backend}/tests/__init__.py +0 -0
- /package/templates/project/{web/backend → backend}/tests/conftest.py.hbs +0 -0
- /package/templates/project/{web/backend/alembic/versions → mobile}/.gitkeep +0 -0
- /package/templates/project/web/{frontend → apps/web}/src/components/.gitkeep +0 -0
- /package/templates/project/web/{frontend → apps/web}/src/main.ts +0 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# 凭证强度审计 (只读, 全脱敏)
|
|
3
|
+
#
|
|
4
|
+
# 检查范围 (7 类):
|
|
5
|
+
# 1. .env 文件: PASSWORD/SECRET/TOKEN/KEY/AK/SK 字段
|
|
6
|
+
# 2. docker 容器内联 env (Config.Env)
|
|
7
|
+
# 3. Redis 容器 --requirepass 启动参数
|
|
8
|
+
# 4. Postgres 容器 POSTGRES_PASSWORD
|
|
9
|
+
# 5. frps/frpc auth.token (toml/ini)
|
|
10
|
+
# 6. DolphinDB 配置文件 password
|
|
11
|
+
# 7. SSH 私钥 类型 + 位数 (~/.ssh/id_*)
|
|
12
|
+
#
|
|
13
|
+
# 强制脱敏: 只输出派生指标 (len/classes/strength), 永不输出明文
|
|
14
|
+
# 字典词: 内置 ~30 个最常见弱口令
|
|
15
|
+
|
|
16
|
+
TARGET="$1"; shift
|
|
17
|
+
SSH_OPTS=("$@")
|
|
18
|
+
|
|
19
|
+
ssh "${SSH_OPTS[@]}" "${TARGET}" bash -s <<'REMOTE'
|
|
20
|
+
# ---------- 强度评估函数 ----------
|
|
21
|
+
# 输入: 一个秘密字符串(从 stdin)
|
|
22
|
+
# 输出: "len=N classes=K strength=STRONG/MEDIUM/WEAK reason=xxx"
|
|
23
|
+
# 不打印明文, 不写文件
|
|
24
|
+
strength_of() {
|
|
25
|
+
local val
|
|
26
|
+
IFS= read -r val
|
|
27
|
+
local len=${#val}
|
|
28
|
+
|
|
29
|
+
# 字典词内置(精简, 30 个最常见弱口令; 大小写不敏感子串匹配)
|
|
30
|
+
local weak_dict='password|passwd|123456|123123|qwerty|abc123|admin|administrator|root|test|demo|guest|user|welcome|letmein|changeme|secret|default|temp|public|private|master|sample|example|prod|dev|staging|backup|hello|monkey'
|
|
31
|
+
local is_dict=0
|
|
32
|
+
echo "$val" | grep -qiE "($weak_dict)" && is_dict=1
|
|
33
|
+
|
|
34
|
+
# 字符类
|
|
35
|
+
local has_upper=0 has_lower=0 has_digit=0 has_symbol=0
|
|
36
|
+
[[ "$val" =~ [A-Z] ]] && has_upper=1
|
|
37
|
+
[[ "$val" =~ [a-z] ]] && has_lower=1
|
|
38
|
+
[[ "$val" =~ [0-9] ]] && has_digit=1
|
|
39
|
+
[[ "$val" =~ [^a-zA-Z0-9] ]] && has_symbol=1
|
|
40
|
+
local classes=$(( has_upper + has_lower + has_digit + has_symbol ))
|
|
41
|
+
|
|
42
|
+
# 评级
|
|
43
|
+
local strength reason
|
|
44
|
+
if [[ "$len" -lt 8 ]]; then strength="WEAK"; reason="len<8"
|
|
45
|
+
elif [[ "$is_dict" == "1" ]]; then strength="WEAK"; reason="dict_match"
|
|
46
|
+
elif [[ "$len" -lt 16 ]]; then strength="MEDIUM"; reason="len<16"
|
|
47
|
+
elif [[ "$classes" -lt 3 ]]; then strength="MEDIUM"; reason="classes<3"
|
|
48
|
+
else strength="STRONG"; reason="ok"
|
|
49
|
+
fi
|
|
50
|
+
printf "len=%d classes=%d strength=%s reason=%s" "$len" "$classes" "$strength" "$reason"
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# 排除明显是"数值/枚举配置参数"而非凭证的 key 名
|
|
54
|
+
# (子串匹配 TOKEN/KEY 会误命中 ACCESS_TOKEN_EXPIRE_MINUTES / KEY_TIMEOUT 这种)
|
|
55
|
+
is_config_not_secret() {
|
|
56
|
+
local key="$1"
|
|
57
|
+
echo "$key" | grep -qiE '(EXPIRE|EXPIRY|TTL|TIMEOUT|SECONDS|MINUTES|HOURS|DAYS|COUNT|LIMIT|RETRY|MAX|MIN|SIZE|LENGTH|INTERVAL|DELAY|ENABLED|DISABLED|MODE|TYPE|VERSION|PORT|HOST|URL|PATH|FILE|DIR)$|_(EXPIRE|TTL|TIMEOUT|SECONDS|MINUTES|HOURS|DAYS|COUNT|LIMIT|MAX|MIN|SIZE|MODE|TYPE)_'
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# 包装: 给定 key+value, 加上"高敏字段长度门槛 32"判定
|
|
61
|
+
evaluate_secret() {
|
|
62
|
+
local key="$1"
|
|
63
|
+
local val="$2"
|
|
64
|
+
[[ -z "$val" ]] && { echo " (空值)"; return; }
|
|
65
|
+
|
|
66
|
+
# 判定是否真凭证字段 (key 名含 PASSWORD/SECRET/ACCESS_KEY_SECRET 等明确含义的)
|
|
67
|
+
# 这类即使值是纯数字短串(如 DolphinDB 默认 123456) 也必须评估为 WEAK 而非 SKIP
|
|
68
|
+
local is_true_credential=0
|
|
69
|
+
echo "$key" | grep -qiE '(PASSWORD|PASSWD|SECRET|PRIVATE[_-]?KEY|ACCESS[_-]?KEY[_-]?SECRET|_SK$|^SK_)' && is_true_credential=1
|
|
70
|
+
|
|
71
|
+
# Bug fix 1: 跳过明显是配置参数的 key (含 EXPIRE/TTL/TIMEOUT/MAX/MIN 等)
|
|
72
|
+
# 但真凭证字段不跳过
|
|
73
|
+
if [[ "$is_true_credential" == "0" ]] && is_config_not_secret "$key"; then
|
|
74
|
+
printf " %-30s [i] SKIP: 看起来是配置参数(非凭证), key 名含 EXPIRE/TTL/TIMEOUT/MAX 等\n" "$key"
|
|
75
|
+
return
|
|
76
|
+
fi
|
|
77
|
+
# Bug fix 1.b: 纯数字短值(像 30 / 86400 这种 TTL/expire) 跳过
|
|
78
|
+
# 但真凭证字段不跳过 (DolphinDB 默认 123456 / Redis 默认 123 这种弱密码必须报)
|
|
79
|
+
if [[ "$is_true_credential" == "0" ]] && [[ "$val" =~ ^[0-9]+$ ]] && [[ ${#val} -lt 10 ]]; then
|
|
80
|
+
printf " %-30s [i] SKIP: 纯数字短值(看起来是配置参数, len=%d)\n" "$key" "${#val}"
|
|
81
|
+
return
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
# 是否高敏字段
|
|
85
|
+
local high_sens=0
|
|
86
|
+
echo "$key" | grep -qiE 'SECRET|JWT|TOKEN|API[_-]?KEY|ACCESS[_-]?KEY[_-]?SECRET|PRIVATE[_-]?KEY|_AK$|_SK$|^AK_|^SK_' && high_sens=1
|
|
87
|
+
|
|
88
|
+
local result
|
|
89
|
+
result=$(printf '%s\n' "$val" | strength_of)
|
|
90
|
+
local len=$(echo "$result" | grep -oE 'len=[0-9]+' | cut -d= -f2)
|
|
91
|
+
local classes=$(echo "$result" | grep -oE 'classes=[0-9]+' | cut -d= -f2)
|
|
92
|
+
local strength=$(echo "$result" | grep -oE 'strength=[A-Z]+' | cut -d= -f2)
|
|
93
|
+
|
|
94
|
+
# Bug fix 2: 长度 >= 24 即视为 STRONG (即使 classes 少, 熵足够)
|
|
95
|
+
# 例如 48 字符 base64 / 64 字符 hex 即使只 2 类, 熵 >= 192 bits 远超 128 bits 安全门槛
|
|
96
|
+
if [[ "$strength" != "WEAK" && "$len" -ge 24 ]]; then
|
|
97
|
+
strength="STRONG"
|
|
98
|
+
result="${result/strength=MEDIUM/strength=STRONG}"
|
|
99
|
+
result="${result/reason=classes<3/reason=long_enough}"
|
|
100
|
+
result="${result/reason=len<16/reason=long_enough}"
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
printf " %-30s %s" "$key" "$result"
|
|
104
|
+
# 严重级标记
|
|
105
|
+
if [[ "$strength" == "WEAK" ]]; then
|
|
106
|
+
printf " [!!!] CRITICAL: 弱凭证, 必须立即轮换\n"
|
|
107
|
+
elif [[ "$high_sens" == "1" && "$len" -lt 32 ]]; then
|
|
108
|
+
printf " [!!] HIGH: 高敏字段建议 >= 32 字符随机串\n"
|
|
109
|
+
elif [[ "$strength" == "MEDIUM" ]]; then
|
|
110
|
+
printf " [!] MEDIUM: 建议加长至 16+ 且 3 类字符\n"
|
|
111
|
+
else
|
|
112
|
+
printf " [OK]\n"
|
|
113
|
+
fi
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
# ---------- 1. .env 文件扫描 ----------
|
|
117
|
+
echo "########## 1. .env 文件凭证强度 ##########"
|
|
118
|
+
ENV_FILES=$(sudo -n find /home /opt /srv /root -maxdepth 6 -type f \
|
|
119
|
+
\( -name ".env" -o -name ".env.*" -o -name "*.env" \) 2>/dev/null \
|
|
120
|
+
| grep -vE '\.example|\.template|\.sample|\.bak|\.old|node_modules' \
|
|
121
|
+
| sort -u | head -20)
|
|
122
|
+
[[ -z "$ENV_FILES" ]] && ENV_FILES=$(find /home /opt /srv /root -maxdepth 6 -type f \
|
|
123
|
+
\( -name ".env" -o -name ".env.*" -o -name "*.env" \) 2>/dev/null \
|
|
124
|
+
| grep -vE '\.example|\.template|\.sample|\.bak|\.old|node_modules' \
|
|
125
|
+
| sort -u | head -20)
|
|
126
|
+
|
|
127
|
+
if [[ -z "$ENV_FILES" ]]; then
|
|
128
|
+
echo "[i] 未找到 .env 文件"
|
|
129
|
+
else
|
|
130
|
+
for f in $ENV_FILES; do
|
|
131
|
+
echo ""
|
|
132
|
+
echo "[$f]"
|
|
133
|
+
# 只挑密码类字段, 解析 KEY=VALUE
|
|
134
|
+
if ! sudo -n cat "$f" 2>/dev/null | head -1 >/dev/null && ! cat "$f" 2>/dev/null | head -1 >/dev/null; then
|
|
135
|
+
echo " [i] 不可读"
|
|
136
|
+
continue
|
|
137
|
+
fi
|
|
138
|
+
(sudo -n cat "$f" 2>/dev/null || cat "$f" 2>/dev/null) \
|
|
139
|
+
| grep -E '^[[:space:]]*[A-Z][A-Z0-9_]*[[:space:]]*=' \
|
|
140
|
+
| grep -iE '(PASSWORD|PASSWD|SECRET|TOKEN|API[_-]?KEY|ACCESS[_-]?KEY|PRIVATE[_-]?KEY|_AK|_SK|^AK_|^SK_)' \
|
|
141
|
+
| while IFS='=' read -r key val; do
|
|
142
|
+
key=$(echo "$key" | tr -d ' \t')
|
|
143
|
+
# 去引号 + 行尾注释
|
|
144
|
+
val=$(echo "$val" | sed -E 's/^[[:space:]]*//; s/[[:space:]]*#.*$//; s/^"//; s/"$//; s/^'\''//; s/'\''$//')
|
|
145
|
+
evaluate_secret "$key" "$val"
|
|
146
|
+
done
|
|
147
|
+
done
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
# ---------- 2. docker 容器内联 env ----------
|
|
151
|
+
echo ""
|
|
152
|
+
echo "########## 2. Docker 容器内联 env 凭证强度 ##########"
|
|
153
|
+
if command -v docker >/dev/null 2>&1; then
|
|
154
|
+
for c in $(docker ps -q 2>/dev/null); do
|
|
155
|
+
name=$(docker inspect -f '{{.Name}}' "$c" 2>/dev/null | sed 's|^/||')
|
|
156
|
+
echo ""
|
|
157
|
+
echo "[$name]"
|
|
158
|
+
docker inspect --format '{{range .Config.Env}}{{println .}}{{end}}' "$c" 2>/dev/null \
|
|
159
|
+
| grep -iE '(PASSWORD|PASSWD|SECRET|TOKEN|API[_-]?KEY|ACCESS[_-]?KEY|PRIVATE[_-]?KEY|_AK=|_SK=|^AK_|^SK_)' \
|
|
160
|
+
| while IFS='=' read -r key val; do
|
|
161
|
+
key=$(echo "$key" | tr -d ' \t')
|
|
162
|
+
evaluate_secret "$key" "$val"
|
|
163
|
+
done
|
|
164
|
+
done
|
|
165
|
+
else
|
|
166
|
+
echo "[i] Docker 未安装"
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
# ---------- 3. Redis requirepass (从启动参数提取) ----------
|
|
170
|
+
echo ""
|
|
171
|
+
echo "########## 3. Redis --requirepass 强度 ##########"
|
|
172
|
+
if command -v docker >/dev/null 2>&1; then
|
|
173
|
+
REDIS_FOUND=0
|
|
174
|
+
for c in $(docker ps -q 2>/dev/null); do
|
|
175
|
+
image=$(docker inspect -f '{{.Config.Image}}' "$c" 2>/dev/null)
|
|
176
|
+
if echo "$image" | grep -qi "redis"; then
|
|
177
|
+
REDIS_FOUND=1
|
|
178
|
+
name=$(docker inspect -f '{{.Name}}' "$c" 2>/dev/null | sed 's|^/||')
|
|
179
|
+
echo ""
|
|
180
|
+
echo "[$name]"
|
|
181
|
+
args=$(docker inspect -f '{{range .Args}}{{.}} {{end}}' "$c" 2>/dev/null)
|
|
182
|
+
pass=$(echo "$args" | grep -oE -- '--requirepass [^ ]+' | head -1 | awk '{print $2}')
|
|
183
|
+
if [[ -n "$pass" ]]; then
|
|
184
|
+
evaluate_secret "REDIS_PASSWORD(--requirepass)" "$pass"
|
|
185
|
+
else
|
|
186
|
+
echo " [!!!] CRITICAL: Redis 未设置 --requirepass"
|
|
187
|
+
fi
|
|
188
|
+
fi
|
|
189
|
+
done
|
|
190
|
+
[[ "$REDIS_FOUND" == "0" ]] && echo "[i] 未发现 Redis 容器"
|
|
191
|
+
fi
|
|
192
|
+
|
|
193
|
+
# ---------- 4. Postgres POSTGRES_PASSWORD (容器 env, 已在 2 覆盖, 此处单独 highlight) ----------
|
|
194
|
+
echo ""
|
|
195
|
+
echo "########## 4. Postgres POSTGRES_PASSWORD 强度 ##########"
|
|
196
|
+
if command -v docker >/dev/null 2>&1; then
|
|
197
|
+
PG_FOUND=0
|
|
198
|
+
for c in $(docker ps -q 2>/dev/null); do
|
|
199
|
+
image=$(docker inspect -f '{{.Config.Image}}' "$c" 2>/dev/null)
|
|
200
|
+
if echo "$image" | grep -qi "postgres"; then
|
|
201
|
+
PG_FOUND=1
|
|
202
|
+
name=$(docker inspect -f '{{.Name}}' "$c" 2>/dev/null | sed 's|^/||')
|
|
203
|
+
echo ""
|
|
204
|
+
echo "[$name]"
|
|
205
|
+
pass=$(docker inspect --format '{{range .Config.Env}}{{println .}}{{end}}' "$c" 2>/dev/null \
|
|
206
|
+
| grep '^POSTGRES_PASSWORD=' | head -1 | cut -d= -f2-)
|
|
207
|
+
if [[ -n "$pass" ]]; then
|
|
208
|
+
evaluate_secret "POSTGRES_PASSWORD" "$pass"
|
|
209
|
+
else
|
|
210
|
+
echo " [!!!] CRITICAL: 未取得 POSTGRES_PASSWORD (是否 secrets 挂载或 trust 认证?)"
|
|
211
|
+
fi
|
|
212
|
+
fi
|
|
213
|
+
done
|
|
214
|
+
[[ "$PG_FOUND" == "0" ]] && echo "[i] 未发现 Postgres 容器"
|
|
215
|
+
fi
|
|
216
|
+
|
|
217
|
+
# ---------- 5. frp auth.token ----------
|
|
218
|
+
echo ""
|
|
219
|
+
echo "########## 5. frps/frpc auth.token 强度 ##########"
|
|
220
|
+
FRP_FILES=$(sudo -n find /etc /opt /home /root -maxdepth 6 -type f \
|
|
221
|
+
\( -name "frps.toml" -o -name "frpc.toml" -o -name "frps.ini" -o -name "frpc.ini" \) 2>/dev/null \
|
|
222
|
+
| grep -vE '\.bak|\.old|\.example' | sort -u | head -10)
|
|
223
|
+
[[ -z "$FRP_FILES" ]] && FRP_FILES=$(find /etc /opt /home /root -maxdepth 6 -type f \
|
|
224
|
+
\( -name "frps.toml" -o -name "frpc.toml" -o -name "frps.ini" -o -name "frpc.ini" \) 2>/dev/null \
|
|
225
|
+
| grep -vE '\.bak|\.old|\.example' | sort -u | head -10)
|
|
226
|
+
|
|
227
|
+
if [[ -z "$FRP_FILES" ]]; then
|
|
228
|
+
echo "[i] 未发现 frp 配置文件"
|
|
229
|
+
else
|
|
230
|
+
for f in $FRP_FILES; do
|
|
231
|
+
echo ""
|
|
232
|
+
echo "[$f]"
|
|
233
|
+
# 提 token = "xxx" / token = xxx / auth.token = xxx
|
|
234
|
+
# 注意: 必须用 { ...; } | grep 而非 cat || cat | grep
|
|
235
|
+
# 后者因 | 优先级高于 ||, 当 sudo 成功时只跑 sudo cat, 后续 pipeline 被丢弃,
|
|
236
|
+
# 导致 token 变量 = 整个文件内容, evaluate_secret 用 read 取第一行 (常是 bindPort=7000)
|
|
237
|
+
token=$( { sudo -n cat "$f" 2>/dev/null || cat "$f" 2>/dev/null; } \
|
|
238
|
+
| grep -iE '^[[:space:]]*(auth\.)?token[[:space:]]*=' \
|
|
239
|
+
| head -1 | sed -E 's/.*=//; s/^[[:space:]]*//; s/[[:space:]]*$//; s/^"//; s/"$//; s/^'\''//; s/'\''$//')
|
|
240
|
+
if [[ -n "$token" ]]; then
|
|
241
|
+
evaluate_secret "frp.token" "$token"
|
|
242
|
+
else
|
|
243
|
+
echo " [!!] HIGH: 未配 token (frp 无认证, 任何人可注册到此 frps)"
|
|
244
|
+
fi
|
|
245
|
+
done
|
|
246
|
+
fi
|
|
247
|
+
|
|
248
|
+
# ---------- 6. DolphinDB 密码 ----------
|
|
249
|
+
echo ""
|
|
250
|
+
echo "########## 6. DolphinDB 配置密码强度 ##########"
|
|
251
|
+
DDB_FILES=$(sudo -n find /etc /opt /home /root -maxdepth 6 -type f \
|
|
252
|
+
\( -name "dolphindb.cfg" -o -name "cluster.cfg" -o -name "controller.cfg" -o -name "agent.cfg" \) 2>/dev/null \
|
|
253
|
+
| grep -vE '\.bak|\.old|\.example' | sort -u | head -10)
|
|
254
|
+
[[ -z "$DDB_FILES" ]] && DDB_FILES=$(find /etc /opt /home /root -maxdepth 6 -type f \
|
|
255
|
+
\( -name "dolphindb.cfg" -o -name "cluster.cfg" -o -name "controller.cfg" -o -name "agent.cfg" \) 2>/dev/null \
|
|
256
|
+
| grep -vE '\.bak|\.old|\.example' | sort -u | head -10)
|
|
257
|
+
|
|
258
|
+
if [[ -z "$DDB_FILES" ]]; then
|
|
259
|
+
echo "[i] 未发现 DolphinDB 配置文件"
|
|
260
|
+
else
|
|
261
|
+
for f in $DDB_FILES; do
|
|
262
|
+
echo ""
|
|
263
|
+
echo "[$f]"
|
|
264
|
+
# DolphinDB 用户密码通常在 users.cfg 或通过 createUser 写入, 这里只看配置文件里的
|
|
265
|
+
# adminPassword / password / encryptedPassword 等字段
|
|
266
|
+
found=0
|
|
267
|
+
while IFS='=' read -r key val; do
|
|
268
|
+
key=$(echo "$key" | tr -d ' \t')
|
|
269
|
+
val=$(echo "$val" | sed -E 's/^[[:space:]]*//; s/[[:space:]]*$//; s/^"//; s/"$//')
|
|
270
|
+
[[ -n "$val" ]] && { evaluate_secret "$key" "$val"; found=1; }
|
|
271
|
+
done < <(sudo -n grep -iE '(password|passwd|admin[Pp]ass)' "$f" 2>/dev/null \
|
|
272
|
+
|| grep -iE '(password|passwd|admin[Pp]ass)' "$f" 2>/dev/null)
|
|
273
|
+
[[ "$found" == "0" ]] && echo " [i] 配置文件中未直接含密码字段(可能用 users.cfg 或 createUser 写入)"
|
|
274
|
+
done
|
|
275
|
+
fi
|
|
276
|
+
|
|
277
|
+
# ---------- 7. SSH 私钥强度 ----------
|
|
278
|
+
echo ""
|
|
279
|
+
echo "########## 7. SSH 私钥类型 / 位数 ##########"
|
|
280
|
+
# 当前用户 + root 的 ~/.ssh/ (Bug fix 3: 去重, root 用户登录时 $HOME=/root 会重复)
|
|
281
|
+
SSH_DIRS=$(printf '%s\n' "$HOME/.ssh" /root/.ssh /home/*/.ssh 2>/dev/null | sort -u)
|
|
282
|
+
for ssh_dir in $SSH_DIRS; do
|
|
283
|
+
[[ -d "$ssh_dir" ]] || continue
|
|
284
|
+
for kf in "$ssh_dir"/id_rsa "$ssh_dir"/id_ed25519 "$ssh_dir"/id_ecdsa "$ssh_dir"/id_dsa; do
|
|
285
|
+
[[ -f "$kf" ]] || continue
|
|
286
|
+
info=$(ssh-keygen -l -f "$kf" 2>/dev/null || sudo -n ssh-keygen -l -f "$kf" 2>/dev/null)
|
|
287
|
+
if [[ -z "$info" ]]; then
|
|
288
|
+
echo " $kf [i] 无法读取(权限?)"
|
|
289
|
+
continue
|
|
290
|
+
fi
|
|
291
|
+
bits=$(echo "$info" | awk '{print $1}')
|
|
292
|
+
type=$(echo "$info" | grep -oE '\([A-Z0-9]+\)$' | tr -d '()')
|
|
293
|
+
perm=$(stat -c '%a' "$kf" 2>/dev/null)
|
|
294
|
+
|
|
295
|
+
case "$type" in
|
|
296
|
+
ED25519)
|
|
297
|
+
printf " %-40s type=%-8s bits=%-4s perm=%s [OK]\n" "$kf" "$type" "$bits" "$perm" ;;
|
|
298
|
+
RSA)
|
|
299
|
+
if [[ "$bits" -ge 4096 ]]; then mark="[OK]"
|
|
300
|
+
elif [[ "$bits" -ge 3072 ]]; then mark="[OK] (建议升 ed25519)"
|
|
301
|
+
elif [[ "$bits" -ge 2048 ]]; then mark="[!] MEDIUM: RSA-${bits} 可接受但建议升 ed25519 或 RSA-4096"
|
|
302
|
+
else mark="[!!!] CRITICAL: RSA-${bits} 已不安全, 立即轮换"
|
|
303
|
+
fi
|
|
304
|
+
printf " %-40s type=%-8s bits=%-4s perm=%s %s\n" "$kf" "$type" "$bits" "$perm" "$mark" ;;
|
|
305
|
+
ECDSA)
|
|
306
|
+
printf " %-40s type=%-8s bits=%-4s perm=%s [!] MEDIUM: ECDSA 存疑(NIST 曲线), 建议 ed25519\n" "$kf" "$type" "$bits" "$perm" ;;
|
|
307
|
+
DSA)
|
|
308
|
+
printf " %-40s type=%-8s bits=%-4s perm=%s [!!!] CRITICAL: DSA 已废弃, 立即轮换\n" "$kf" "$type" "$bits" "$perm" ;;
|
|
309
|
+
*)
|
|
310
|
+
printf " %-40s type=%-8s bits=%-4s perm=%s\n" "$kf" "$type" "$bits" "$perm" ;;
|
|
311
|
+
esac
|
|
312
|
+
|
|
313
|
+
# 权限检查
|
|
314
|
+
if [[ -n "$perm" && "$perm" != "600" && "$perm" != "400" ]]; then
|
|
315
|
+
echo " [!!] HIGH: 私钥权限 $perm 过宽, 应 600"
|
|
316
|
+
fi
|
|
317
|
+
done
|
|
318
|
+
done
|
|
319
|
+
|
|
320
|
+
echo ""
|
|
321
|
+
echo "########## 凭证强度汇总 ##########"
|
|
322
|
+
echo "[i] 修复指引:"
|
|
323
|
+
echo " - WEAK 密码: 立即轮换 + 走 dwy-server-security 规则 (>= 16 字符 + 三类字符)"
|
|
324
|
+
echo " - 高敏字段 (JWT/SECRET/AK/SK): >= 32 字符随机串"
|
|
325
|
+
echo " - 生成命令: openssl rand -base64 24 | tr -d '=+/' | cut -c1-20"
|
|
326
|
+
echo " - 高敏: openssl rand -hex 32 (生成 64 字符 hex token)"
|
|
327
|
+
REMOTE
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# 依赖服务连通性 / 防火墙
|
|
3
|
+
TARGET="$1"; shift
|
|
4
|
+
SSH_OPTS=("$@")
|
|
5
|
+
|
|
6
|
+
ssh "${SSH_OPTS[@]}" "${TARGET}" bash -s <<'REMOTE'
|
|
7
|
+
echo "--- 防火墙状态 ---"
|
|
8
|
+
if command -v ufw >/dev/null 2>&1; then
|
|
9
|
+
echo "[ufw]"
|
|
10
|
+
sudo -n ufw status verbose 2>/dev/null || echo " [!] 需 sudo 查看 ufw"
|
|
11
|
+
fi
|
|
12
|
+
if command -v firewall-cmd >/dev/null 2>&1; then
|
|
13
|
+
echo "[firewalld]"
|
|
14
|
+
sudo -n firewall-cmd --state 2>/dev/null
|
|
15
|
+
sudo -n firewall-cmd --list-all 2>/dev/null
|
|
16
|
+
fi
|
|
17
|
+
echo "[iptables]"
|
|
18
|
+
sudo -n iptables -L INPUT -n --line-numbers 2>/dev/null | head -20 || echo " [i] 无法读取 iptables"
|
|
19
|
+
|
|
20
|
+
echo ""
|
|
21
|
+
echo "--- 监听端口 (按地址分组) ---"
|
|
22
|
+
(ss -tlnp 2>/dev/null || netstat -tlnp 2>/dev/null) | awk 'NR>1 {print $4}' | awk -F: '{
|
|
23
|
+
port=$NF;
|
|
24
|
+
ip=substr($0,1,length($0)-length(port)-1);
|
|
25
|
+
if (ip=="0.0.0.0" || ip=="*" || ip=="[::]") public[port]++;
|
|
26
|
+
else if (ip=="127.0.0.1" || ip=="[::1]") loop[port]++;
|
|
27
|
+
else internal[port]++;
|
|
28
|
+
}
|
|
29
|
+
END {
|
|
30
|
+
print "公网监听 (0.0.0.0 / *):"
|
|
31
|
+
for (p in public) print " " p
|
|
32
|
+
print "内网监听 (具体 IP):"
|
|
33
|
+
for (p in internal) print " " p
|
|
34
|
+
print "回环监听 (127.0.0.1):"
|
|
35
|
+
for (p in loop) print " " p
|
|
36
|
+
}'
|
|
37
|
+
|
|
38
|
+
echo ""
|
|
39
|
+
echo "--- 系统负载与内存 ---"
|
|
40
|
+
uptime
|
|
41
|
+
free -h 2>/dev/null | head -3
|
|
42
|
+
|
|
43
|
+
echo ""
|
|
44
|
+
echo "--- 磁盘 ---"
|
|
45
|
+
df -h | grep -vE "tmpfs|udev" | head -10
|
|
46
|
+
|
|
47
|
+
echo ""
|
|
48
|
+
echo "--- 已安装的 Web 应用进程 (常见) ---"
|
|
49
|
+
ps -eo user,pid,%cpu,%mem,cmd --sort=-%mem 2>/dev/null \
|
|
50
|
+
| grep -E "(nginx|node|python|uvicorn|gunicorn|java|php-fpm|redis-server|postgres|mysql|mongod)" \
|
|
51
|
+
| grep -v grep | head -20
|
|
52
|
+
|
|
53
|
+
echo ""
|
|
54
|
+
echo "--- 应用健康检查端点 (常见路径自测) ---"
|
|
55
|
+
NGINX_DUMP=$(sudo -n nginx -T 2>/dev/null || nginx -T 2>/dev/null || true)
|
|
56
|
+
DOMAINS=$(echo "$NGINX_DUMP" | awk '/server_name/ {for(i=2;i<=NF;i++) print $i}' | tr -d ';' | sort -u | grep -v "^_$" | head -3)
|
|
57
|
+
[[ -z "$DOMAINS" ]] && DOMAINS="localhost"
|
|
58
|
+
for D in $DOMAINS; do
|
|
59
|
+
for path in "/health" "/api/health" "/healthz" "/_health" "/status"; do
|
|
60
|
+
code=$(curl -k -sS -o /dev/null -w "%{http_code}" --max-time 3 "https://$D$path" 2>/dev/null || echo "000")
|
|
61
|
+
[[ "$code" != "404" && "$code" != "000" ]] && printf " https://%s%s -> %s\n" "$D" "$path" "$code"
|
|
62
|
+
done
|
|
63
|
+
done
|
|
64
|
+
|
|
65
|
+
echo ""
|
|
66
|
+
echo "--- 系统更新提示 ---"
|
|
67
|
+
if command -v apt >/dev/null 2>&1; then
|
|
68
|
+
sudo -n apt list --upgradable 2>/dev/null | head -10 || echo " [i] 无法检查 apt 更新"
|
|
69
|
+
elif command -v yum >/dev/null 2>&1; then
|
|
70
|
+
sudo -n yum check-update 2>/dev/null | head -10 || echo " [i] 无法检查 yum 更新"
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
echo ""
|
|
74
|
+
echo "--- 内核/系统启动时间 ---"
|
|
75
|
+
who -b 2>/dev/null
|
|
76
|
+
echo "kernel: $(uname -r)"
|
|
77
|
+
REMOTE
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# SSH 安全检查 (只读)
|
|
3
|
+
TARGET="$1"; shift
|
|
4
|
+
SSH_OPTS=("$@")
|
|
5
|
+
|
|
6
|
+
ssh "${SSH_OPTS[@]}" "${TARGET}" bash -s <<'REMOTE'
|
|
7
|
+
echo "--- /etc/ssh/sshd_config (filtered key directives) ---"
|
|
8
|
+
SSHD_CONF="/etc/ssh/sshd_config"
|
|
9
|
+
SSHD_DIR="/etc/ssh/sshd_config.d"
|
|
10
|
+
if [[ -r "$SSHD_CONF" ]]; then
|
|
11
|
+
grep -Ei "^[[:space:]]*(PermitRootLogin|PasswordAuthentication|PubkeyAuthentication|PermitEmptyPasswords|Port|Protocol|MaxAuthTries|LoginGraceTime|AllowUsers|AllowGroups|DenyUsers|DenyGroups|ChallengeResponseAuthentication|UsePAM|X11Forwarding|ClientAliveInterval|ClientAliveCountMax|LogLevel|Banner)" "$SSHD_CONF" 2>/dev/null
|
|
12
|
+
if [[ -d "$SSHD_DIR" ]]; then
|
|
13
|
+
echo "--- $SSHD_DIR/*.conf ---"
|
|
14
|
+
grep -EiH "^[[:space:]]*(PermitRootLogin|PasswordAuthentication|PubkeyAuthentication|PermitEmptyPasswords|Port|Protocol|MaxAuthTries)" "$SSHD_DIR"/*.conf 2>/dev/null
|
|
15
|
+
fi
|
|
16
|
+
else
|
|
17
|
+
echo "[!] $SSHD_CONF 不可读 (需要 sudo)"
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
echo ""
|
|
21
|
+
echo "--- effective sshd config (sshd -T, 需 sudo) ---"
|
|
22
|
+
sudo -n sshd -T 2>/dev/null | grep -Ei "^(permitrootlogin|passwordauthentication|pubkeyauthentication|permitemptypasswords|port|protocol|maxauthtries|logingracetime|allowusers|allowgroups|x11forwarding|loglevel)" || echo "[i] 无法读取 effective config (需要 sudo 或 sshd -T 不可用)"
|
|
23
|
+
|
|
24
|
+
echo ""
|
|
25
|
+
echo "--- ssh process & version ---"
|
|
26
|
+
sshd -V 2>&1 | head -2 || ssh -V 2>&1
|
|
27
|
+
|
|
28
|
+
echo ""
|
|
29
|
+
echo "--- recent failed logins (last 20) ---"
|
|
30
|
+
sudo -n journalctl _COMM=sshd --since "7 days ago" 2>/dev/null | grep -Ei "failed|invalid" | tail -20 \
|
|
31
|
+
|| sudo -n grep -Ei "failed|invalid" /var/log/auth.log 2>/dev/null | tail -20 \
|
|
32
|
+
|| echo "[i] 无法读取认证日志 (需要 sudo)"
|
|
33
|
+
|
|
34
|
+
echo ""
|
|
35
|
+
echo "--- fail2ban status ---"
|
|
36
|
+
if command -v fail2ban-client >/dev/null 2>&1; then
|
|
37
|
+
sudo -n fail2ban-client status 2>/dev/null || echo "[i] fail2ban 已安装,但需要 sudo 查看状态"
|
|
38
|
+
else
|
|
39
|
+
echo "[!] fail2ban 未安装"
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
echo ""
|
|
43
|
+
echo "--- authorized_keys 概览 (当前用户) ---"
|
|
44
|
+
if [[ -r ~/.ssh/authorized_keys ]]; then
|
|
45
|
+
awk '{print " key: " $1 " " substr($2,1,16) "... " $3}' ~/.ssh/authorized_keys
|
|
46
|
+
echo " total: $(wc -l < ~/.ssh/authorized_keys) keys"
|
|
47
|
+
else
|
|
48
|
+
echo "[i] 当前用户无 authorized_keys"
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
echo ""
|
|
52
|
+
echo "--- /root/.ssh/authorized_keys (需 sudo) ---"
|
|
53
|
+
sudo -n test -f /root/.ssh/authorized_keys 2>/dev/null && \
|
|
54
|
+
echo " [!] root 用户存在 authorized_keys, 共 $(sudo -n wc -l < /root/.ssh/authorized_keys 2>/dev/null) 行" \
|
|
55
|
+
|| echo "[i] 无 root authorized_keys 或无 sudo 权限"
|
|
56
|
+
REMOTE
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# dwy-deploy-audit: 一键全量巡检(subagent 不可用时的串行/并行退化方案)
|
|
3
|
+
#
|
|
4
|
+
# 优先做法是让 Claude 用 subagent 并行派遣多个 Agent 跑各类 check_xxx.sh
|
|
5
|
+
# (见 SKILL.md Step 4)。本脚本仅作为本地一键回退使用。
|
|
6
|
+
#
|
|
7
|
+
# 用法:
|
|
8
|
+
# ./run_all.sh <user>@<host> [-p <port>] [-i <key>] [--serial]
|
|
9
|
+
#
|
|
10
|
+
# 默认 8 路并行。--serial 强制串行。
|
|
11
|
+
|
|
12
|
+
set -uo pipefail
|
|
13
|
+
|
|
14
|
+
if [[ $# -lt 1 ]]; then
|
|
15
|
+
echo "Usage: $0 <user>@<host|alias> [-p <port>] [-i <key>] [--serial]" >&2
|
|
16
|
+
exit 1
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
TARGET="$1"; shift
|
|
20
|
+
SSH_OPTS=(-o ConnectTimeout=10 -o BatchMode=yes -o StrictHostKeyChecking=accept-new)
|
|
21
|
+
PARALLEL=true
|
|
22
|
+
|
|
23
|
+
while [[ $# -gt 0 ]]; do
|
|
24
|
+
case "$1" in
|
|
25
|
+
-p) SSH_OPTS+=(-p "$2"); shift 2;;
|
|
26
|
+
-i) SSH_OPTS+=(-i "$2"); shift 2;;
|
|
27
|
+
--serial) PARALLEL=false; shift;;
|
|
28
|
+
*) echo "Unknown arg: $1" >&2; exit 1;;
|
|
29
|
+
esac
|
|
30
|
+
done
|
|
31
|
+
|
|
32
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
33
|
+
TMP_DIR=$(mktemp -d -t dwy-audit-XXXXXX)
|
|
34
|
+
trap 'rm -rf "$TMP_DIR"' EXIT
|
|
35
|
+
|
|
36
|
+
# 检查项: <section_name>:<script_name>
|
|
37
|
+
SECTIONS=(
|
|
38
|
+
"BASE_INFO:check_base.sh"
|
|
39
|
+
"SSH:check_ssh.sh"
|
|
40
|
+
"NGINX:check_nginx.sh"
|
|
41
|
+
"DB:check_db.sh"
|
|
42
|
+
"HTTPS:check_https.sh"
|
|
43
|
+
"ENV:check_env.sh"
|
|
44
|
+
"DOCKER:check_docker.sh"
|
|
45
|
+
"SERVICES:check_services.sh"
|
|
46
|
+
"RESILIENCE:check_resilience.sh"
|
|
47
|
+
"LOGS:check_logs.sh"
|
|
48
|
+
"CAPACITY:check_capacity.sh"
|
|
49
|
+
"SECRETS:check_secrets.sh"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
echo "========================================"
|
|
53
|
+
echo " dwy-deploy-audit | target: ${TARGET}"
|
|
54
|
+
echo " timestamp: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
55
|
+
echo " mode: $([ "$PARALLEL" = true ] && echo "parallel (${#SECTIONS[@]} workers)" || echo 'serial')"
|
|
56
|
+
echo "========================================"
|
|
57
|
+
|
|
58
|
+
run_section() {
|
|
59
|
+
local name="$1"
|
|
60
|
+
local script="$2"
|
|
61
|
+
local out_file="${TMP_DIR}/${name}.out"
|
|
62
|
+
{
|
|
63
|
+
echo "########## SECTION: ${name} ##########"
|
|
64
|
+
bash "${SCRIPT_DIR}/${script}" "${TARGET}" "${SSH_OPTS[@]}" 2>&1 \
|
|
65
|
+
|| echo "[!] ${name} 部分失败"
|
|
66
|
+
echo "########## END: ${name} ##########"
|
|
67
|
+
} > "$out_file" 2>&1
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if [[ "$PARALLEL" == "true" ]]; then
|
|
71
|
+
# 并行:所有 section 后台启动
|
|
72
|
+
pids=()
|
|
73
|
+
for entry in "${SECTIONS[@]}"; do
|
|
74
|
+
name="${entry%%:*}"
|
|
75
|
+
script="${entry##*:}"
|
|
76
|
+
run_section "$name" "$script" &
|
|
77
|
+
pids+=($!)
|
|
78
|
+
done
|
|
79
|
+
|
|
80
|
+
# 等所有完成
|
|
81
|
+
for pid in "${pids[@]}"; do
|
|
82
|
+
wait "$pid"
|
|
83
|
+
done
|
|
84
|
+
|
|
85
|
+
# 按顺序输出结果(保证报告分类有序)
|
|
86
|
+
for entry in "${SECTIONS[@]}"; do
|
|
87
|
+
name="${entry%%:*}"
|
|
88
|
+
echo ""
|
|
89
|
+
cat "${TMP_DIR}/${name}.out"
|
|
90
|
+
done
|
|
91
|
+
else
|
|
92
|
+
# 串行
|
|
93
|
+
for entry in "${SECTIONS[@]}"; do
|
|
94
|
+
name="${entry%%:*}"
|
|
95
|
+
script="${entry##*:}"
|
|
96
|
+
echo ""
|
|
97
|
+
run_section "$name" "$script"
|
|
98
|
+
cat "${TMP_DIR}/${name}.out"
|
|
99
|
+
done
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
echo ""
|
|
103
|
+
echo "========================================"
|
|
104
|
+
echo " 巡检数据采集完成,请由 Claude 分析生成报告"
|
|
105
|
+
echo "========================================"
|