修复开发容器启动并加固部署配置
This commit is contained in:
@@ -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
|
||||
@@ -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]:
|
||||
if ip_location and _is_non_china_ip(ip_location):
|
||||
return {"category": "ABNORMAL_IP", "severity": "HIGH"}
|
||||
path = (log.path or "").lower()
|
||||
if any(marker in path for marker in SENSITIVE_PROBE_MARKERS):
|
||||
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:
|
||||
return {"category": "SERVER_ERROR", "severity": "HIGH"}
|
||||
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"
|
||||
|
||||
|
||||
@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
|
||||
async def test_security_access_logs_classify_non_china_ip_as_abnormal(db_session, monkeypatch):
|
||||
"""安全事件明细应把非中国公网 IP 归类为异常 IP。"""
|
||||
|
||||
+12
-1
@@ -19,7 +19,18 @@ services:
|
||||
container_name: ctms_frontend_dev
|
||||
restart: always
|
||||
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:
|
||||
VITE_RUNTIME_ENV: ${ENV:-development}
|
||||
VITE_ALLOW_INSECURE_DEV_LOGIN: ${VITE_ALLOW_INSECURE_DEV_LOGIN:-true}
|
||||
|
||||
@@ -11,8 +11,6 @@ services:
|
||||
volumes:
|
||||
# 持久化数据到 Linux 本地,防止重启丢失
|
||||
- ./pg_data:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "5432:5432"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ctms_user -d ctms_db"]
|
||||
interval: 5s
|
||||
@@ -28,8 +26,6 @@ services:
|
||||
pull: false
|
||||
container_name: ctms_backend
|
||||
restart: always
|
||||
ports:
|
||||
- "8000:8000"
|
||||
environment:
|
||||
DATABASE_URL: postgresql+asyncpg://ctms_user:secret_password@db/ctms_db
|
||||
ENV: ${ENV:-development}
|
||||
|
||||
@@ -52,6 +52,12 @@ http {
|
||||
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/ {
|
||||
proxy_pass http://backend;
|
||||
proxy_set_header Host $host;
|
||||
|
||||
@@ -50,6 +50,12 @@ http {
|
||||
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/ {
|
||||
proxy_pass http://backend;
|
||||
proxy_set_header Host $host;
|
||||
|
||||
@@ -552,9 +552,10 @@ PY
|
||||
|
||||
check_http_endpoint() {
|
||||
local path="$1"
|
||||
local max_attempts="${2:-10}"
|
||||
local url="${BASE_URL%/}$path"
|
||||
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 …"
|
||||
sleep "$warmup_delay"
|
||||
while [[ "$attempt" -le "$max_attempts" ]]; do
|
||||
@@ -584,6 +585,9 @@ run_health_checks() {
|
||||
step "探测 HTTP 接口可用性"
|
||||
check_http_endpoint "/health"
|
||||
check_http_endpoint "/api/v1/auth/login-key"
|
||||
if [[ "$TARGET_ENV" == "dev" ]]; then
|
||||
check_http_endpoint "/" 60
|
||||
fi
|
||||
ok "HTTP 接口全部就绪"
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user