feat(gateway): API Key 인증 + HTTPS (Caddy) 보안 강화
- gateway.py: auth middleware — /api/* 엔드포인트에 Bearer token 필수 - Caddyfile: Let's Encrypt 자동 HTTPS 리버스 프록시 - docker-compose.yml: Caddy 추가, Gateway 포트 내부 전용 - config.py: GATEWAY_API_KEY 설정 추가 - .env: 키 생성 명령어 가이드 포함
This commit is contained in:
29
gateway.py
29
gateway.py
@@ -28,11 +28,12 @@ logger = logging.getLogger(__name__)
|
||||
class GatewayAPI:
|
||||
"""HTTP API server for Collector ↔ Gateway communication."""
|
||||
|
||||
def __init__(self, bot, host: str = "0.0.0.0", port: int = 8585):
|
||||
def __init__(self, bot, host: str = "0.0.0.0", port: int = 8585, api_key: str = ""):
|
||||
self.bot = bot
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.app = web.Application()
|
||||
self.api_key = api_key
|
||||
self.app = web.Application(middlewares=[self._auth_middleware])
|
||||
self._setup_routes()
|
||||
|
||||
# In-memory stores (Gateway is stateless across restarts)
|
||||
@@ -47,6 +48,27 @@ class GatewayAPI:
|
||||
self.app.router.add_post("/api/register", self._post_register)
|
||||
self.app.router.add_get("/api/commands/{project}", self._get_commands)
|
||||
|
||||
# ─── Auth Middleware ───
|
||||
|
||||
@web.middleware
|
||||
async def _auth_middleware(self, request: web.Request, handler):
|
||||
"""Reject requests without valid API key on /api/* routes."""
|
||||
# Health endpoint is public
|
||||
if request.path == "/health":
|
||||
return await handler(request)
|
||||
|
||||
# All /api/* routes require auth
|
||||
if request.path.startswith("/api/") and self.api_key:
|
||||
auth = request.headers.get("Authorization", "")
|
||||
if auth != f"Bearer {self.api_key}":
|
||||
logger.warning(f"[GATEWAY] 401 Unauthorized: {request.method} {request.path} from {request.remote}")
|
||||
return web.json_response(
|
||||
{"error": "Unauthorized", "detail": "Invalid or missing API key"},
|
||||
status=401,
|
||||
)
|
||||
|
||||
return await handler(request)
|
||||
|
||||
# ─── Health ───
|
||||
|
||||
async def _health(self, request: web.Request) -> web.Response:
|
||||
@@ -170,4 +192,5 @@ class GatewayAPI:
|
||||
await runner.setup()
|
||||
site = web.TCPSite(runner, self.host, self.port)
|
||||
await site.start()
|
||||
logger.info(f"[GATEWAY] HTTP API started on {self.host}:{self.port}")
|
||||
auth_status = "API Key enabled" if self.api_key else "⚠️ NO AUTH (set GATEWAY_API_KEY!)"
|
||||
logger.info(f"[GATEWAY] HTTP API started on {self.host}:{self.port} [{auth_status}]")
|
||||
|
||||
Reference in New Issue
Block a user