diff --git a/deployment/aws/lambda/Dockerfile b/deployment/aws/lambda/Dockerfile index 159b58c60..c8992bcb1 100644 --- a/deployment/aws/lambda/Dockerfile +++ b/deployment/aws/lambda/Dockerfile @@ -1,9 +1,12 @@ -ARG PYTHON_VERSION=3.10 +ARG PYTHON_VERSION=3.12 FROM --platform=linux/amd64 public.ecr.aws/lambda/python:${PYTHON_VERSION} WORKDIR /tmp +# Install system dependencies to compile (numexpr) +RUN dnf install -y gcc-c++ && dnf clean all + RUN pip install pip -U # Install TiTiler from local source. This is necessary because # pyproject.toml refers to TiTiler dependencies (core, extensions, @@ -11,6 +14,9 @@ RUN pip install pip -U COPY . /titiler RUN pip install "/titiler/src/titiler/application" "mangum>=0.10.0" -t /asset --no-binary pydantic +RUN mkdir -p /asset/lib +RUN cp /lib64/libexpat.so.1 /asset/lib/ + # Reduce package size and remove useless files RUN cd /asset && find . -type f -name '*.pyc' | while read f; do n=$(echo $f | sed 's/__pycache__\///' | sed 's/.cpython-[0-9]*//'); cp $f $n; done; RUN cd /asset && find . -type d -a -name '__pycache__' -print0 | xargs -0 rm -rf diff --git a/dockerfiles/Dockerfile.gunicorn b/dockerfiles/Dockerfile.gunicorn index 6df319f04..fea976777 100644 --- a/dockerfiles/Dockerfile.gunicorn +++ b/dockerfiles/Dockerfile.gunicorn @@ -15,7 +15,7 @@ ENV CURL_CA_BUNDLE /etc/ssl/certs/ca-certificates.crt COPY src/titiler/ /tmp/titiler/ RUN python -m pip install -U pip RUN python -m pip install /tmp/titiler/core /tmp/titiler/extensions["cogeo,stac"] /tmp/titiler/mosaic /tmp/titiler/application --no-cache-dir --upgrade -RUN rm -rf /tmp/titiler +# RUN rm -rf /tmp/titiler ENV MODULE_NAME titiler.application.main ENV VARIABLE_NAME app diff --git a/pyproject.toml b/pyproject.toml index 03df56013..01bf1e34f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ name = "titiler" description = "A modern dynamic tile server built on top of FastAPI and Rasterio/GDAL." readme = "README.md" -requires-python = ">=3.8" +requires-python = "3.12" license = {file = "LICENSE"} authors = [ {name = "Vincent Sarago", email = "vincent@developmentseed.com"}, @@ -31,10 +31,10 @@ version="0.11.6" dependencies = [ # Configured for volume-mount path of Lambda Dockerfile. For local # development, update accordingly for your local filesystem path: - "titiler.core @ file:///titiler/src/titiler/core", - "titiler.extensions @ file:///titiler/src/titiler/extensions", - "titiler.mosaic @ file:///titiler/src/titiler/mosaic", - "titiler.application @ file:///titiler/src/titiler/application", + "titiler.core @ file:///titiler/src//titiler/core", + "titiler.extensions @ file:///titiler/src//titiler/extensions", + "titiler.mosaic @ file:///titiler/src//titiler/mosaic", + "titiler.application @ file:///titiler/src//titiler/application", ] [project.optional-dependencies] diff --git a/src/titiler/application/pyproject.toml b/src/titiler/application/pyproject.toml index 3312f5f5f..e9afaf07d 100644 --- a/src/titiler/application/pyproject.toml +++ b/src/titiler/application/pyproject.toml @@ -31,9 +31,9 @@ dynamic = ["version"] dependencies = [ # Configured for volume-mount path of Lambda Dockerfile. For local # development, update accordingly for your local filesystem path: - "titiler.core @ file:///titiler/src/titiler/core", - "titiler.extensions[cogeo,stac] @ file:///titiler/src/titiler/extensions", - "titiler.mosaic @ file:///titiler/src/titiler/mosaic", + "titiler.core @ file:///titiler/src//titiler/core", + "titiler.extensions[cogeo,stac] @ file:///titiler/src//titiler/extensions", + "titiler.mosaic @ file:///titiler/src//titiler/mosaic", "starlette-cramjam>=0.3,<0.4", "python-dotenv", ] diff --git a/src/titiler/application/titiler/application/main.py b/src/titiler/application/titiler/application/main.py index 557450ed9..8930469e2 100644 --- a/src/titiler/application/titiler/application/main.py +++ b/src/titiler/application/titiler/application/main.py @@ -25,6 +25,7 @@ LoggerMiddleware, LowerCaseQueryStringMiddleware, TotalTimeMiddleware, + BlockVRTMiddleware, ) from titiler.extensions import ( cogValidateExtension, @@ -173,6 +174,7 @@ if api_settings.lower_case_query_parameters: app.add_middleware(LowerCaseQueryStringMiddleware) +app.add_middleware(BlockVRTMiddleware) router = APIRouter(prefix=global_prefix) diff --git a/src/titiler/core/titiler/core/middleware.py b/src/titiler/core/titiler/core/middleware.py index f592c962d..fceb51b8b 100644 --- a/src/titiler/core/titiler/core/middleware.py +++ b/src/titiler/core/titiler/core/middleware.py @@ -6,10 +6,12 @@ from typing import Optional, Set from fastapi.logger import logger +import rasterio from starlette.datastructures import MutableHeaders from starlette.requests import Request from starlette.types import ASGIApp, Message, Receive, Scope, Send - +from starlette.middleware.base import BaseHTTPMiddleware +from starlette.responses import Response class CacheControlMiddleware: """MiddleWare to add CacheControl in response headers.""" @@ -166,3 +168,41 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send): request.scope["query_string"] = query_string.encode(DECODE_FORMAT) await self.app(scope, receive, send) + +class BlockVRTMiddleware(BaseHTTPMiddleware): + """Middleware to block requests with vrt in the path.""" + async def dispatch(self, request: Request, call_next): + """Handle call.""" + # Check path for VRT + logger.info(f"Processing request: {request.url}") + if ".vrt" in request.scope["path"].lower(): + return Response( + content="VRT files are not supported.", + status_code=403, + ) + + # Check URL parameter for VRT + url_param = request.query_params.get("url", "").lower() + if ".vrt" in url_param: + return Response( + content="VRT files are not supported in URL parameters.", + status_code=403, + ) + + # TODO: check if we need this. + if url_param.startswith(("http://", "https://", "s3://", "gs://", "azure://")): + + try: + with rasterio.open(url_param, sharing=False) as src: + if src.driver == "VRT": + return Response( + content="VRT files are not supported in URL parameters.", + status_code=403, + ) + except Exception: + # If rasterio fails to open the file, we assume it's not a VRT + pass + + # Continue with the request if no VRT detected + response = await call_next(request) + return response diff --git a/src/titiler/extensions/pyproject.toml b/src/titiler/extensions/pyproject.toml index 525cf0d71..04da04010 100644 --- a/src/titiler/extensions/pyproject.toml +++ b/src/titiler/extensions/pyproject.toml @@ -31,7 +31,7 @@ dynamic = ["version"] dependencies = [ # Configured for volume-mount path of Lambda Dockerfile. For local # development, update accordingly for your local filesystem path: - "titiler.core @ file:///titiler/src/titiler/core", + "titiler.core @ file:///titiler/src//titiler/core", ] [project.optional-dependencies] diff --git a/src/titiler/mosaic/pyproject.toml b/src/titiler/mosaic/pyproject.toml index 3d0265f5e..e3e1290ae 100644 --- a/src/titiler/mosaic/pyproject.toml +++ b/src/titiler/mosaic/pyproject.toml @@ -31,7 +31,7 @@ dynamic = ["version"] dependencies = [ # Configured for volume-mount path of Lambda Dockerfile. For local # development, update accordingly for your local filesystem path: - "titiler.core @ file:///titiler/src/titiler/core", + "titiler.core @ file:///titiler/src//titiler/core", "cogeo-mosaic>=5.0,<5.2", ]