보안 강화: CORS 제한, Path Traversal 방어, 헬스체크 추가
- travel-proxy: get_thumb NameError 수정 및 경로 조작 방어 - stock-lab, music-lab: CORS allow_origins=* → 환경변수 기반 도메인 제한 - travel-proxy, deployer: /health 엔드포인트 추가 - 전 서비스 .dockerignore 추가 (.git, __pycache__, .env 제외) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
6
backend/.dockerignore
Normal file
6
backend/.dockerignore
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
.git
|
||||||
|
__pycache__
|
||||||
|
*.pyc
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
*.md
|
||||||
6
deployer/.dockerignore
Normal file
6
deployer/.dockerignore
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
.git
|
||||||
|
__pycache__
|
||||||
|
*.pyc
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
*.md
|
||||||
@@ -32,6 +32,10 @@ def run_deploy_script():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f"Exception during deployment: {e}")
|
logger.exception(f"Exception during deployment: {e}")
|
||||||
|
|
||||||
|
@app.get("/health")
|
||||||
|
def health():
|
||||||
|
return {"status": "healthy", "service": "deployer"}
|
||||||
|
|
||||||
@app.post("/webhook")
|
@app.post("/webhook")
|
||||||
async def webhook(req: Request, background_tasks: BackgroundTasks):
|
async def webhook(req: Request, background_tasks: BackgroundTasks):
|
||||||
body = await req.body()
|
body = await req.body()
|
||||||
|
|||||||
6
music-lab/.dockerignore
Normal file
6
music-lab/.dockerignore
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
.git
|
||||||
|
__pycache__
|
||||||
|
*.pyc
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
*.md
|
||||||
@@ -16,11 +16,13 @@ from .db import (
|
|||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
_cors_origins = os.getenv("CORS_ALLOW_ORIGINS", "http://localhost:3007,http://localhost:8080").split(",")
|
||||||
app.add_middleware(
|
app.add_middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
allow_origins=["*"],
|
allow_origins=[o.strip() for o in _cors_origins],
|
||||||
allow_methods=["*"],
|
allow_credentials=False,
|
||||||
allow_headers=["*"],
|
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
||||||
|
allow_headers=["Content-Type"],
|
||||||
)
|
)
|
||||||
|
|
||||||
MUSIC_AI_SERVER_URL = os.getenv("MUSIC_AI_SERVER_URL", "")
|
MUSIC_AI_SERVER_URL = os.getenv("MUSIC_AI_SERVER_URL", "")
|
||||||
|
|||||||
6
stock-lab/.dockerignore
Normal file
6
stock-lab/.dockerignore
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
.git
|
||||||
|
__pycache__
|
||||||
|
*.pyc
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
*.md
|
||||||
@@ -23,12 +23,13 @@ from .price_fetcher import get_current_prices
|
|||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
# CORS 설정 (프론트엔드 접근 허용)
|
# CORS 설정 (프론트엔드 접근 허용)
|
||||||
|
_cors_origins = os.getenv("CORS_ALLOW_ORIGINS", "http://localhost:3007,http://localhost:8080").split(",")
|
||||||
app.add_middleware(
|
app.add_middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
allow_origins=["*"], # 운영 시에는 구체적인 도메인으로 제한하는 것이 좋음
|
allow_origins=[o.strip() for o in _cors_origins],
|
||||||
allow_credentials=True,
|
allow_credentials=False,
|
||||||
allow_methods=["*"],
|
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
||||||
allow_headers=["*"],
|
allow_headers=["Content-Type"],
|
||||||
)
|
)
|
||||||
|
|
||||||
scheduler = BackgroundScheduler(timezone=os.getenv("TZ", "Asia/Seoul"))
|
scheduler = BackgroundScheduler(timezone=os.getenv("TZ", "Asia/Seoul"))
|
||||||
|
|||||||
6
travel-proxy/.dockerignore
Normal file
6
travel-proxy/.dockerignore
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
.git
|
||||||
|
__pycache__
|
||||||
|
*.pyc
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
*.md
|
||||||
@@ -168,6 +168,10 @@ def scan_album(album: str) -> List[Dict[str, Any]]:
|
|||||||
# -----------------------------
|
# -----------------------------
|
||||||
# Routes
|
# Routes
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
|
@app.get("/health")
|
||||||
|
def health():
|
||||||
|
return {"status": "healthy", "service": "travel-proxy"}
|
||||||
|
|
||||||
@app.get("/api/travel/regions")
|
@app.get("/api/travel/regions")
|
||||||
def regions():
|
def regions():
|
||||||
_meta_changed_invalidate_cache()
|
_meta_changed_invalidate_cache()
|
||||||
@@ -239,12 +243,18 @@ def photos(
|
|||||||
|
|
||||||
@app.get("/media/travel/.thumb/{album}/{filename}")
|
@app.get("/media/travel/.thumb/{album}/{filename}")
|
||||||
def get_thumb(album: str, filename: str):
|
def get_thumb(album: str, filename: str):
|
||||||
|
if ".." in album or ".." in filename:
|
||||||
|
raise HTTPException(400, "Invalid path")
|
||||||
|
|
||||||
src = (ROOT / album / filename).resolve()
|
src = (ROOT / album / filename).resolve()
|
||||||
|
if not str(src).startswith(str(ROOT)):
|
||||||
|
raise HTTPException(403, "Access denied")
|
||||||
|
if not src.exists() or not src.is_file():
|
||||||
|
raise HTTPException(404, "Source not found")
|
||||||
|
|
||||||
|
p = ensure_thumb(src, album)
|
||||||
if not p.exists() or not p.is_file():
|
if not p.exists() or not p.is_file():
|
||||||
raise HTTPException(404, "Thumbnail not found")
|
raise HTTPException(404, "Thumbnail not found")
|
||||||
|
|
||||||
# src로부터 thumb 생성/확인 (원본 확장자 유지)
|
|
||||||
p = ensure_thumb(src, album)
|
|
||||||
return FileResponse(str(p))
|
return FileResponse(str(p))
|
||||||
|
|
||||||
@app.get("/api/version")
|
@app.get("/api/version")
|
||||||
|
|||||||
Reference in New Issue
Block a user