chore: polish code with little update (#182)

- Run Docker container as non-root user (appuser) to minimize security risks
- Add Docker HEALTHCHECK for better container orchestration
- Make CORS configurable via ALLOWED_ORIGINS env var with security warning
- Replace assertions with proper error handling (TypeError/ValueError)
- Add 30s timeout to HTTP requests to prevent hanging connections
- Disable auto-reload in production uvicorn settings
This commit is contained in:
Li Yi
2025-10-11 14:49:18 +08:00
committed by GitHub
parent 8177876e5e
commit 9cea7f9314
8 changed files with 46 additions and 21 deletions

View File

@@ -8,6 +8,15 @@ RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
COPY ./api /app/api
ENV PORT=80
# Create non-root user
RUN groupadd -r appuser && useradd -r -g appuser appuser && \
chown -R appuser:appuser /app
USER appuser
ENV PORT=8080
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8080/api/v1/models').read()"
CMD ["sh", "-c", "uvicorn api.app:app --host 0.0.0.0 --port ${PORT}"]

View File

@@ -1,4 +1,5 @@
import logging
import os
import uvicorn
from fastapi import FastAPI
@@ -23,9 +24,16 @@ logging.basicConfig(
)
app = FastAPI(**config)
allowed_origins = os.environ.get("ALLOWED_ORIGINS", "*")
origins_list = [origin.strip() for origin in allowed_origins.split(",")] if allowed_origins != "*" else ["*"]
# Warn if CORS allows all origins
if origins_list == ["*"]:
logging.warning("CORS is configured to allow all origins (*). Set ALLOWED_ORIGINS environment variable to restrict access.")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_origins=origins_list, # nosec - configurable via ALLOWED_ORIGINS env var
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
@@ -61,4 +69,5 @@ async def validation_exception_handler(request, exc):
handler = Mangum(app)
if __name__ == "__main__":
uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=True)
# Bind to 0.0.0.0 for container environments, network is handled by network policies and load balancers
uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=False) # nosec B104

View File

@@ -7,8 +7,6 @@ from botocore.exceptions import ClientError
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from api.setting import DEFAULT_API_KEYS
api_key_param = os.environ.get("API_KEY_PARAM_NAME")
api_key_secret_arn = os.environ.get("API_KEY_SECRET_ARN")
api_key_env = os.environ.get("API_KEY")
@@ -31,8 +29,9 @@ elif api_key_secret_arn:
elif api_key_env:
api_key = api_key_env
else:
# For local use only.
api_key = DEFAULT_API_KEYS
raise RuntimeError(
"API Key is not configured. Please set up your API Key."
)
security = HTTPBearer()

View File

@@ -310,7 +310,8 @@ class BedrockModel(BaseChatModel):
if message.role != "system":
# ignore system messages here
continue
assert isinstance(message.content, str)
if not isinstance(message.content, str):
raise TypeError(f"System message content must be a string, got {type(message.content).__name__}")
system_prompts.append({"text": message.content})
return system_prompts
@@ -580,7 +581,8 @@ class BedrockModel(BaseChatModel):
tool_config["toolChoice"] = {"auto": {}}
else:
# Specific tool to use
assert "function" in chat_request.tool_choice
if "function" not in chat_request.tool_choice:
raise ValueError("tool_choice must contain 'function' key when specifying a specific tool")
tool_config["toolChoice"] = {"tool": {"name": chat_request.tool_choice["function"].get("name", "")}}
args["toolConfig"] = tool_config
# add Additional fields to enable extend thinking
@@ -784,7 +786,7 @@ class BedrockModel(BaseChatModel):
return base64.b64decode(image_data), content_type.group(1)
# Send a request to the image URL
response = requests.get(image_url)
response = requests.get(image_url, timeout=30)
# Check if the request was successful
if response.status_code == 200:
content_type = response.headers.get("Content-Type")

View File

@@ -1,7 +1,5 @@
import os
DEFAULT_API_KEYS = "bedrock"
API_ROUTE_PREFIX = os.environ.get("API_ROUTE_PREFIX", "/api/v1")
TITLE = "Amazon Bedrock Proxy APIs"