修复开发容器启动并加固部署配置

This commit is contained in:
Cheng Zhou
2026-06-22 15:35:00 +08:00
parent 360a2ba2b1
commit 6056c8364a
8 changed files with 88 additions and 8 deletions
+22
View File
@@ -0,0 +1,22 @@
.git
.gitignore
.worktrees
.agents
.claude
.DS_Store
.env
.env.*
**/.env
**/.env.*
pg_data
tmp
.install-logs
.pytest_cache
**/.pytest_cache
**/__pycache__
**/*.pyc
frontend/node_modules
frontend/dist
backend/.coverage
backend/.pytest_cache
nginx/certs
+2 -2
View File
@@ -399,11 +399,11 @@ def _is_non_china_ip(ip_location: IpLocation) -> bool:
def _classify_security_access_log(log: SecurityAccessLog, ip_location: IpLocation | None = None) -> dict[str, str]: def _classify_security_access_log(log: SecurityAccessLog, ip_location: IpLocation | None = None) -> dict[str, str]:
if ip_location and _is_non_china_ip(ip_location):
return {"category": "ABNORMAL_IP", "severity": "HIGH"}
path = (log.path or "").lower() path = (log.path or "").lower()
if any(marker in path for marker in SENSITIVE_PROBE_MARKERS): if any(marker in path for marker in SENSITIVE_PROBE_MARKERS):
return {"category": "PROBE", "severity": "CRITICAL"} return {"category": "PROBE", "severity": "CRITICAL"}
if ip_location and _is_non_china_ip(ip_location):
return {"category": "ABNORMAL_IP", "severity": "HIGH"}
if log.status_code >= 500: if log.status_code >= 500:
return {"category": "SERVER_ERROR", "severity": "HIGH"} return {"category": "SERVER_ERROR", "severity": "HIGH"}
if log.auth_status == "INVALID_TOKEN": if log.auth_status == "INVALID_TOKEN":
@@ -929,6 +929,41 @@ async def test_security_access_logs_classify_sensitive_path_probe(db_session):
assert result["items"][0]["severity"] == "CRITICAL" assert result["items"][0]["severity"] == "CRITICAL"
@pytest.mark.asyncio
async def test_security_access_logs_prioritize_sensitive_probe_over_foreign_ip(db_session, monkeypatch):
"""敏感路径探测应优先于海外 IP 分类,避免降低风险等级。"""
await db_session.execute(text("DELETE FROM security_access_logs"))
monkeypatch.setattr(
permission_monitoring,
"resolve_ip_location",
lambda ip: FakeIpInfo("South Holland", "", "", "Netherlands"),
)
await db_session.execute(
text(
"""
INSERT INTO security_access_logs
(id, method, path, status_code, elapsed_ms, client_ip, user_agent, auth_status, user_identifier, created_at)
VALUES
('00000000-0000-4000-8000-000000000505', 'GET', '/api/.git/config', 404, 0.7,
'45.148.10.95', 'probe-bot/1.0', 'ANONYMOUS', NULL, CURRENT_TIMESTAMP)
"""
)
)
await db_session.commit()
result = await permission_monitoring.get_security_access_logs(
db=db_session,
_=AdminUserStub(),
status_min=400,
auth_status=None,
page=1,
page_size=20,
)
assert result["items"][0]["category"] == "PROBE"
assert result["items"][0]["severity"] == "CRITICAL"
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_security_access_logs_classify_non_china_ip_as_abnormal(db_session, monkeypatch): async def test_security_access_logs_classify_non_china_ip_as_abnormal(db_session, monkeypatch):
"""安全事件明细应把非中国公网 IP 归类为异常 IP。""" """安全事件明细应把非中国公网 IP 归类为异常 IP。"""
+12 -1
View File
@@ -19,7 +19,18 @@ services:
container_name: ctms_frontend_dev container_name: ctms_frontend_dev
restart: always restart: always
working_dir: /app working_dir: /app
command: sh -c "npm ci && npm run dev -- --host 0.0.0.0" command:
- sh
- -c
- |
set -e
lock_hash="$$(sha256sum package-lock.json | awk '{print $$1}')"
marker="node_modules/.ctms-package-lock.sha256"
if [ ! -f "$$marker" ] || [ "$$(cat "$$marker")" != "$$lock_hash" ]; then
npm ci
printf '%s' "$$lock_hash" > "$$marker"
fi
npm run dev -- --host 0.0.0.0
environment: environment:
VITE_RUNTIME_ENV: ${ENV:-development} VITE_RUNTIME_ENV: ${ENV:-development}
VITE_ALLOW_INSECURE_DEV_LOGIN: ${VITE_ALLOW_INSECURE_DEV_LOGIN:-true} VITE_ALLOW_INSECURE_DEV_LOGIN: ${VITE_ALLOW_INSECURE_DEV_LOGIN:-true}
-4
View File
@@ -11,8 +11,6 @@ services:
volumes: volumes:
# 持久化数据到 Linux 本地,防止重启丢失 # 持久化数据到 Linux 本地,防止重启丢失
- ./pg_data:/var/lib/postgresql/data - ./pg_data:/var/lib/postgresql/data
ports:
- "5432:5432"
healthcheck: healthcheck:
test: ["CMD-SHELL", "pg_isready -U ctms_user -d ctms_db"] test: ["CMD-SHELL", "pg_isready -U ctms_user -d ctms_db"]
interval: 5s interval: 5s
@@ -28,8 +26,6 @@ services:
pull: false pull: false
container_name: ctms_backend container_name: ctms_backend
restart: always restart: always
ports:
- "8000:8000"
environment: environment:
DATABASE_URL: postgresql+asyncpg://ctms_user:secret_password@db/ctms_db DATABASE_URL: postgresql+asyncpg://ctms_user:secret_password@db/ctms_db
ENV: ${ENV:-development} ENV: ${ENV:-development}
+6
View File
@@ -52,6 +52,12 @@ http {
add_header Cache-Control "public, max-age=31536000, immutable" always; add_header Cache-Control "public, max-age=31536000, immutable" always;
} }
location ~* (^|/)(\.git|\.env($|[./_-])|wp-config\.php|config\.php|database\.yml|backup($|[._/-])) {
access_log off;
log_not_found off;
return 404;
}
location /api/ { location /api/ {
proxy_pass http://backend; proxy_pass http://backend;
proxy_set_header Host $host; proxy_set_header Host $host;
+6
View File
@@ -50,6 +50,12 @@ http {
add_header Cache-Control "public, max-age=86400" always; add_header Cache-Control "public, max-age=86400" always;
} }
location ~* (^|/)(\.git|\.env($|[./_-])|wp-config\.php|config\.php|database\.yml|backup($|[._/-])) {
access_log off;
log_not_found off;
return 404;
}
location /api/ { location /api/ {
proxy_pass http://backend; proxy_pass http://backend;
proxy_set_header Host $host; proxy_set_header Host $host;
+5 -1
View File
@@ -552,9 +552,10 @@ PY
check_http_endpoint() { check_http_endpoint() {
local path="$1" local path="$1"
local max_attempts="${2:-10}"
local url="${BASE_URL%/}$path" local url="${BASE_URL%/}$path"
log "探测接口: $url" log "探测接口: $url"
local attempt=1 max_attempts=10 delay=2 warmup_delay=3 local attempt=1 delay=2 warmup_delay=3
log "等待服务预热 ${warmup_delay}s …" log "等待服务预热 ${warmup_delay}s …"
sleep "$warmup_delay" sleep "$warmup_delay"
while [[ "$attempt" -le "$max_attempts" ]]; do while [[ "$attempt" -le "$max_attempts" ]]; do
@@ -584,6 +585,9 @@ run_health_checks() {
step "探测 HTTP 接口可用性" step "探测 HTTP 接口可用性"
check_http_endpoint "/health" check_http_endpoint "/health"
check_http_endpoint "/api/v1/auth/login-key" check_http_endpoint "/api/v1/auth/login-key"
if [[ "$TARGET_ENV" == "dev" ]]; then
check_http_endpoint "/" 60
fi
ok "HTTP 接口全部就绪" ok "HTTP 接口全部就绪"
} }