3개 서비스(system1/system2/system3 web)에서 Dockerfile, nginx.conf, docker-compose.yml이 외부 노출되는 취약점 수정. [구조 수정] - Dockerfile: COPY . → COPY public/ (정적 파일만 웹루트에 복사) - system3 uploads: plain prefix → ^~ (regex deny 우선순위 충돌 방지) [nginx deny (defense in depth)] - exact match: /Dockerfile, /docker-compose.yml, /nginx.conf, /.env, /.gitignore - prefix: ^~ /.git/ 디렉토리 전체 차단 - regex: 하위 경로 + 변형 대비 [CI 보안 게이트] - scripts/check-webroot-security.sh: 화이트리스트 방식, find + exact match Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
95 lines
2.1 KiB
Bash
Executable File
95 lines
2.1 KiB
Bash
Executable File
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
# =============================================================================
|
|
# 웹루트 보안 검증 스크립트
|
|
# 배포 후 실행: docker 이미지 내 /usr/share/nginx/html에 허용된 파일만 있는지 확인
|
|
# 화이트리스트 방식 — 허용되지 않은 파일이 있으면 FAIL
|
|
# =============================================================================
|
|
|
|
SERVICES=("system1-web" "system2-web" "system3-web")
|
|
|
|
# 허용 목록 (줄바꿈 구분 — 공백 파일명 안전)
|
|
ALLOWED_system1_web="index.html
|
|
manifest.json
|
|
sw.js
|
|
logo.png
|
|
components
|
|
css
|
|
img
|
|
js
|
|
pages
|
|
static"
|
|
|
|
ALLOWED_system2_web="push-sw.js
|
|
css
|
|
img
|
|
js
|
|
pages"
|
|
|
|
ALLOWED_system3_web="ai-assistant.html
|
|
app.html
|
|
favicon.ico
|
|
issue-view.html
|
|
issues-archive.html
|
|
issues-dashboard.html
|
|
issues-inbox.html
|
|
issues-management.html
|
|
m
|
|
push-sw.js
|
|
reports-daily.html
|
|
reports-monthly.html
|
|
reports-weekly.html
|
|
reports.html
|
|
static
|
|
sw.js
|
|
uploads"
|
|
|
|
FAIL=0
|
|
|
|
for service in "${SERVICES[@]}"; do
|
|
varname="ALLOWED_${service//-/_}"
|
|
allowed="${!varname}"
|
|
|
|
echo "Checking $service..."
|
|
|
|
# 컨테이너 생성만 (실행 안 함) → exec으로 검사 → 제거
|
|
docker compose create --no-deps "$service" >/dev/null 2>&1
|
|
container=$(docker compose ps -q "$service" | head -n1)
|
|
if [ -z "$container" ]; then
|
|
echo " FAIL: container not found for $service"
|
|
FAIL=1; continue
|
|
fi
|
|
|
|
entries=$(docker exec "$container" \
|
|
find /usr/share/nginx/html -maxdepth 1 -mindepth 1 -printf '%f\n' 2>/dev/null || true)
|
|
docker compose rm -f "$service" >/dev/null 2>&1
|
|
|
|
# 빈 webroot 체크 (COPY public/ 실패 감지)
|
|
if [ -z "$entries" ]; then
|
|
echo " FAIL: $service webroot is empty"
|
|
FAIL=1; continue
|
|
fi
|
|
|
|
while IFS= read -r f; do
|
|
[ -z "$f" ] && continue
|
|
# -xF: 정확히 일치하는 줄만 (substring 매칭 방지)
|
|
if ! echo "$allowed" | grep -qxF "$f"; then
|
|
echo " FAIL: unexpected file in webroot → $f"
|
|
FAIL=1
|
|
fi
|
|
done <<< "$entries"
|
|
|
|
if [ $FAIL -eq 0 ]; then
|
|
echo " OK"
|
|
fi
|
|
done
|
|
|
|
echo ""
|
|
if [ $FAIL -eq 0 ]; then
|
|
echo "✓ All web roots clean"
|
|
else
|
|
echo "✗ Security check FAILED — fix before deploying"
|
|
fi
|
|
exit $FAIL
|